Skip to content
This repository was archived by the owner on Oct 19, 2026. It is now read-only.

Commit 7ee4034

Browse files
juanlou1217genedna
andauthored
feat: Implement commit diff file directory tree and detailed change information API (#1711)
* feat: Implement commit diff file directory tree and detailed change information API * feat: Implement commit diff file directory tree and detailed change information API --------- Co-authored-by: Quanyi Ma <eli@patch.sh>
1 parent 6b8daf5 commit 7ee4034

8 files changed

Lines changed: 91 additions & 65 deletions

File tree

‎moon/apps/web/components/ClView/FileChangeTab.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ export const FileChangeTab = React.memo<FileChangeTabProps>(({ id }) => {
2323
<FileDiff
2424
fileChangeData={ClFilesChangedData}
2525
fileChangeIsLoading={isFileChangeLoading}
26-
treeData={treeResponse as CommonResultVecMuiTreeNode}
26+
treeData={treeResponse?.data as CommonResultVecMuiTreeNode['data']}
2727
treeIsLoading={treeIsLoading}
2828
/>
2929
)

‎moon/apps/web/components/CodeView/CommitsView/detail/index.tsx‎

Lines changed: 42 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,21 @@
1-
import React, { useEffect, useMemo, useState } from 'react'
1+
import React, { useMemo } from 'react'
22
import { CopyIcon, FileCodeIcon } from '@primer/octicons-react'
33
import copy from 'copy-to-clipboard'
44
import { format, formatDistance, fromUnixTime } from 'date-fns'
55
import { useAtom } from 'jotai'
66
import { useRouter } from 'next/router'
77
import toast from 'react-hot-toast'
88

9-
import { CommitSummary, CommonPageDiffItem, DiffItem } from '@gitmono/types'
9+
import { CommitSummary, CommonPageDiffItem, CommonResultVecMuiTreeNode, DiffItem } from '@gitmono/types'
1010
import { LoadingSpinner } from '@gitmono/ui'
1111

1212
import { formatAssignees } from '@/components/CodeView/CommitsView'
1313
import { commitPath } from '@/components/CodeView/CommitsView/items'
1414
import FileDiff from '@/components/DiffView/FileDiff'
1515
import { MemberHoverAvatarList } from '@/components/Issues/MemberHoverAvatarList'
1616
import { useScope } from '@/contexts/scope'
17-
import { useGetCommitsDetail } from '@/hooks/commits/useGetCommitsDetail'
17+
import { useGetCommitsMuiTree } from '@/hooks/commits/useGetCommitsMuiTree'
18+
import { usePostCommitsFilesChanged } from '@/hooks/commits/usePostCommitsFilesChanged'
1819

1920
interface CommitDetailData {
2021
commit: CommitSummary
@@ -26,44 +27,42 @@ export const CommitsDetailView: React.FC = () => {
2627
const { sha } = router.query
2728
const { scope } = useScope()
2829

29-
const { mutate: commitDetail, isPending: isLoadingCommitDetail } = useGetCommitsDetail()
30-
3130
const [commitPathValue, _setCommitPath] = useAtom(commitPath)
32-
const [commitsDetail, setCommitsDetail] = useState<CommitDetailData>()
3331

34-
useEffect(() => {
35-
if (!sha || !commitPathValue) return
32+
const commitSha = Array.isArray(sha) ? sha[0] : (sha ?? '')
3633

37-
commitDetail(
38-
{
39-
data: {
40-
path: commitPathValue,
41-
sha: Array.isArray(sha) ? sha[0] : (sha ?? '')
42-
}
43-
},
44-
{
45-
onSuccess: (response) => {
46-
const data = response.data
34+
const commitParams = useMemo(() => {
35+
if (!commitSha || !commitPathValue) return null
36+
return {
37+
path: commitPathValue,
38+
sha: commitSha
39+
}
40+
}, [commitPathValue, commitSha])
4741

48-
if (data) {
49-
setCommitsDetail(data)
50-
}
51-
}
52-
}
53-
)
54-
}, [sha, commitDetail, commitPathValue])
42+
const { data: filesChangedRes, isLoading: isLoadingFilesChanged } = usePostCommitsFilesChanged(commitParams, {
43+
page: 1,
44+
per_page: 100
45+
})
46+
47+
const { data: treeResponse, isLoading: treeIsLoading } = useGetCommitsMuiTree(commitParams)
48+
49+
const commitsDetail: CommitDetailData | undefined = useMemo(() => {
50+
if (!filesChangedRes?.data) return undefined
51+
return {
52+
commit: filesChangedRes.data.commit,
53+
diffs: filesChangedRes.data.page.items as DiffItem[]
54+
}
55+
}, [filesChangedRes])
5556

56-
// Convert diffs to CommonPageDiffItem format
5757
const fileChangeData: CommonPageDiffItem | undefined = useMemo(() => {
5858
if (!commitsDetail?.diffs) return undefined
5959

6060
return {
6161
items: commitsDetail.diffs,
62-
total: commitsDetail.diffs.length
62+
total: filesChangedRes?.data?.page.total ?? commitsDetail.diffs.length
6363
}
64-
}, [commitsDetail])
64+
}, [commitsDetail, filesChangedRes])
6565

66-
// Calculate total stats
6766
const stats = useMemo(() => {
6867
if (!commitsDetail?.diffs) return { additions: 0, deletions: 0, files: 0 }
6968

@@ -89,10 +88,9 @@ export const CommitsDetailView: React.FC = () => {
8988
}
9089
}, [commitsDetail])
9190

92-
const commitSha = Array.isArray(sha) ? sha[0] : (sha ?? '')
9391
const commitDate = commitsDetail?.commit.date ? fromUnixTime(parseInt(commitsDetail.commit.date, 10)) : null
9492

95-
if (isLoadingCommitDetail && !commitsDetail) {
93+
if (isLoadingFilesChanged && !commitsDetail) {
9694
return (
9795
<div className='flex h-[400px] items-center justify-center'>
9896
<LoadingSpinner />
@@ -147,21 +145,27 @@ export const CommitsDetailView: React.FC = () => {
147145
</div>
148146

149147
{commitDate && (
150-
<span className='inline-flex items-center rounded-full border border-gray-300 bg-gray-50 px-2 py-0.5 text-[11px] font-medium text-gray-700'>
151-
GPG Verified{/*{item.Verified}*/}
148+
<span
149+
className={`inline-flex items-center rounded-full border px-2 py-0.5 text-[11px] font-medium ${
150+
commitsDetail.commit.gpg_status === 'Verified'
151+
? 'border-green-600 text-green-600'
152+
: commitsDetail.commit.gpg_status === 'Unverified'
153+
? 'border-yellow-600 text-yellow-600'
154+
: 'border-gray-600 text-gray-600'
155+
}`}
156+
>
157+
{commitsDetail.commit.gpg_status}
152158
</span>
153159
)}
154160
</div>
155161
</div>
156162

157-
{/* File Stats */}
158163
{fileChangeData && (
159164
<div className='flex flex-col px-5'>
160165
<div className='justify-center rounded-lg border border-gray-200'>
161166
<div className='border-b px-4 py-2 text-gray-900'> {commitsDetail.commit.short_message}</div>
162167

163168
<div className='flex items-center justify-between px-4 py-2 text-sm'>
164-
{/* Left side: File stats */}
165169
<div className='flex items-center gap-4'>
166170
<div className='flex items-center gap-2'>
167171
<span className='font-medium text-gray-700'>{stats.files}</span>
@@ -177,7 +181,6 @@ export const CommitsDetailView: React.FC = () => {
177181
</div>
178182
</div>
179183

180-
{/* Right side: Parent commits and current commit */}
181184
<div className='flex items-center gap-2 text-sm text-gray-600'>
182185
{commitsDetail.commit.parents && commitsDetail.commit.parents.length > 0 ? (
183186
<>
@@ -240,13 +243,9 @@ export const CommitsDetailView: React.FC = () => {
240243
{fileChangeData ? (
241244
<FileDiff
242245
fileChangeData={fileChangeData}
243-
fileChangeIsLoading={isLoadingCommitDetail}
244-
treeData={{
245-
data: [],
246-
err_message: '',
247-
req_result: true
248-
}}
249-
treeIsLoading={false}
246+
fileChangeIsLoading={isLoadingFilesChanged}
247+
treeData={treeResponse?.data as CommonResultVecMuiTreeNode['data']}
248+
treeIsLoading={treeIsLoading}
250249
/>
251250
) : (
252251
<div className='flex h-[200px] items-center justify-center text-gray-500'>No file changes</div>

‎moon/apps/web/components/CodeView/CommitsView/index.tsx‎

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { useSetAtom } from 'jotai'
77
import { useRouter } from 'next/router'
88
import toast from 'react-hot-toast'
99

10+
import { GpgStatus } from '@gitmono/types'
1011
import { GitCommitIcon } from '@gitmono/ui'
1112

1213
import { ListBanner } from '@/components/ClView/ClList'
@@ -55,6 +56,7 @@ type Commits = {
5556
author: string
5657
committer: string
5758
date: string
59+
gpg_status?: GpgStatus
5860
parents: string[]
5961
sha: string
6062
short_message: string
@@ -176,11 +178,19 @@ export const CommitsView: React.FC = () => {
176178
key={item.sha}
177179
title={item.short_message}
178180
labels={
179-
<span className='inline-flex items-center rounded-full border border-gray-300 bg-gray-50 px-2 py-0.5 text-[11px] font-medium text-gray-700'>
180-
GPG Verified{/*{item.Verified}*/}
181+
<span
182+
className={`inline-flex items-center rounded-full border px-2 py-0.5 text-[11px] font-medium ${
183+
item.gpg_status === 'Verified'
184+
? 'border-green-600 text-green-600'
185+
: item.gpg_status === 'Unverified'
186+
? 'border-yellow-600 text-yellow-600'
187+
: 'border-gray-600 text-gray-600'
188+
}`}
189+
>
190+
{item.gpg_status}
181191
</span>
182192
}
183-
gitId={
193+
sha={
184194
<span className='items-center font-mono text-xs text-gray-500'>
185195
{item.sha.substring(0, 7)}
186196
</span>

‎moon/apps/web/components/CodeView/CommitsView/items.tsx‎

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,15 @@ export const CommitsItem = memo(
1111
({
1212
title,
1313
children,
14-
gitId,
14+
sha,
1515
labels,
1616
rightIcon,
1717
onClick,
1818
copyIcon
1919
}: {
2020
title: string
2121
children?: ReactNode
22-
gitId?: ReactNode
22+
sha?: ReactNode
2323
labels?: ReactNode
2424
rightIcon?: ReactNode
2525
copyIcon?: ReactNode
@@ -48,31 +48,22 @@ export const CommitsItem = memo(
4848
</div>
4949

5050
<div className='right ml-4 flex items-center gap-2 text-xs text-gray-600'>
51-
{/* Labels (Verified 状态) */}
52-
{labels && (
53-
<div className='flex items-center'>
54-
{/* 移除了内层冗余的 div/padding,直接使用 labels 的样式 */}
55-
{labels}
56-
</div>
57-
)}
51+
{labels && <div className='flex items-center'>{labels}</div>}
5852

59-
{/* Git ID (短 SHA) */}
60-
{gitId && (
53+
{sha && (
6154
<div className='flex items-center'>
6255
<div className='rounded-md px-1.5 py-1 font-mono transition duration-150 ease-in-out hover:bg-gray-100'>
63-
{gitId}
56+
{sha}
6457
</div>
6558
</div>
6659
)}
6760

68-
{/* Copy Icon (复制 SHA) */}
6961
{copyIcon && (
7062
<div className='flex items-center'>
7163
<div className='rounded-md p-1 transition duration-150 ease-in-out hover:bg-gray-100'>{copyIcon}</div>
7264
</div>
7365
)}
7466

75-
{/* Right Icon (箭头/详情图标) */}
7667
{rightIcon && (
7768
<div className='flex items-center'>
7869
<div className='rounded-md p-1 transition duration-150 ease-in-out hover:bg-gray-100'>{rightIcon}</div>

‎moon/apps/web/components/DiffView/FileDiff.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ function generateParsedFiles(diffFiles: { path: string; lang: string; diff: stri
6464
interface FileDiffProps {
6565
fileChangeData: CommonPageDiffItem
6666
fileChangeIsLoading: boolean
67-
treeData: CommonResultVecMuiTreeNode
67+
treeData: CommonResultVecMuiTreeNode['data']
6868
treeIsLoading: boolean
6969
}
7070

‎moon/apps/web/components/DiffView/TreeView/FileTree.tsx‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import { CustomTreeItem } from './CustomTreeItem'
1212
import { convertToTreeData, getDescendantIds } from './TreeUtils'
1313

1414
interface FileTreeProps {
15-
treeData: CommonResultVecMuiTreeNode
15+
treeData: CommonResultVecMuiTreeNode['data']
1616
treeDataLoading: boolean
1717
onFileClick?: (filePath: string) => void
1818
}
@@ -25,8 +25,8 @@ const FileTree = ({ treeData, treeDataLoading, onFileClick }: FileTreeProps) =>
2525

2626
// Process the tree data when API returns
2727
useEffect(() => {
28-
if (treeData?.data) {
29-
const convertedData = convertToTreeData(treeData.data)
28+
if (treeData) {
29+
const convertedData = convertToTreeData(treeData)
3030

3131
setTreeAllData(convertedData)
3232
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { useQuery } from '@tanstack/react-query'
2+
3+
import { GetApiCommitsMuiTreeData, GetApiCommitsMuiTreeParams } from '@gitmono/types/generated'
4+
5+
import { legacyApiClient } from '@/utils/queryClient'
6+
7+
export function useGetCommitsMuiTree(params: GetApiCommitsMuiTreeParams | null) {
8+
return useQuery<GetApiCommitsMuiTreeData>({
9+
queryKey: ['commits-mui-tree', params],
10+
queryFn: () => legacyApiClient.v1.getApiCommitsMuiTree().request(params!),
11+
enabled: Boolean(params?.sha && params?.path)
12+
})
13+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { useQuery } from '@tanstack/react-query'
2+
3+
import { Pagination, PostApiCommitsFilesChangedData, PostApiCommitsFilesChangedParams } from '@gitmono/types/generated'
4+
5+
import { legacyApiClient } from '@/utils/queryClient'
6+
7+
export function usePostCommitsFilesChanged(params: PostApiCommitsFilesChangedParams | null, data: Pagination) {
8+
return useQuery<PostApiCommitsFilesChangedData>({
9+
queryKey: ['commits-files-changed', params, data],
10+
queryFn: () => legacyApiClient.v1.postApiCommitsFilesChanged().request(params!, data),
11+
enabled: Boolean(params?.sha && params?.path)
12+
})
13+
}

0 commit comments

Comments
 (0)