()
| 34 | * Fetches both stats and hunks when component mounts. |
| 35 | */ |
| 36 | export function useDiffData(): DiffData { |
| 37 | const [diffResult, setDiffResult] = useState<GitDiffResult | null>(null) |
| 38 | const [hunks, setHunks] = useState<Map<string, StructuredPatchHunk[]>>( |
| 39 | new Map(), |
| 40 | ) |
| 41 | const [loading, setLoading] = useState(true) |
| 42 | |
| 43 | // Fetch diff data on mount |
| 44 | useEffect(() => { |
| 45 | let cancelled = false |
| 46 | |
| 47 | async function loadDiffData() { |
| 48 | try { |
| 49 | // Fetch both stats and hunks |
| 50 | const [statsResult, hunksResult] = await Promise.all([ |
| 51 | fetchGitDiff(), |
| 52 | fetchGitDiffHunks(), |
| 53 | ]) |
| 54 | |
| 55 | if (!cancelled) { |
| 56 | setDiffResult(statsResult) |
| 57 | setHunks(hunksResult) |
| 58 | setLoading(false) |
| 59 | } |
| 60 | } catch (_error) { |
| 61 | if (!cancelled) { |
| 62 | setDiffResult(null) |
| 63 | setHunks(new Map()) |
| 64 | setLoading(false) |
| 65 | } |
| 66 | } |
| 67 | } |
| 68 | |
| 69 | void loadDiffData() |
| 70 | |
| 71 | return () => { |
| 72 | cancelled = true |
| 73 | } |
| 74 | }, []) |
| 75 | |
| 76 | return useMemo(() => { |
| 77 | if (!diffResult) { |
| 78 | return { stats: null, files: [], hunks: new Map(), loading } |
| 79 | } |
| 80 | |
| 81 | const { stats, perFileStats } = diffResult |
| 82 | const files: DiffFile[] = [] |
| 83 | |
| 84 | // Iterate over perFileStats to get all files including large/skipped ones |
| 85 | for (const [path, fileStats] of perFileStats) { |
| 86 | const fileHunks = hunks.get(path) |
| 87 | const isUntracked = fileStats.isUntracked ?? false |
| 88 | |
| 89 | // Detect large file (in perFileStats but not in hunks, and not binary/untracked) |
| 90 | const isLargeFile = !fileStats.isBinary && !isUntracked && !fileHunks |
| 91 | |
| 92 | // Detect truncated file (total > limit means we truncated) |
| 93 | const totalLines = fileStats.added + fileStats.removed |
no test coverage detected