1import { useEffect, useState, PropsWithChildren } from 'react'; 2import { parseDiff, Diff, Hunk } from 'react-diff-view'; 3 4import { Snippet } from '../Snippet'; 5import { SnippetContent } from '../SnippetContent'; 6import { SnippetHeader } from '../SnippetHeader'; 7 8const randomCommitHash = () => Math.random().toString(36).slice(2, 9); 9 10// These types come from `react-diff-view` library 11type RenderLine = { 12 oldRevision: string; 13 newRevision: string; 14 type: 'unified' | 'split'; 15 hunks: object[]; 16 newPath: string; 17 oldPath: string; 18}; 19 20type Props = PropsWithChildren<{ 21 source?: string; 22 raw?: string; 23}>; 24 25export const DiffBlock = ({ source, raw }: Props) => { 26 const [diff, setDiff] = useState<RenderLine[] | null>(raw ? parseDiff(raw) : null); 27 useEffect(() => { 28 if (source) { 29 const fetchDiffAsync = async () => { 30 const response = await fetch(source); 31 const result = await response.text(); 32 setDiff(parseDiff(result)); 33 }; 34 35 fetchDiffAsync(); 36 } 37 }, [source]); 38 39 if (!diff) { 40 return null; 41 } 42 43 const renderFile = ({ 44 oldRevision = randomCommitHash(), 45 newRevision = randomCommitHash(), 46 type, 47 hunks, 48 newPath, 49 }: RenderLine) => ( 50 <Snippet key={oldRevision + '-' + newRevision}> 51 <SnippetHeader title={newPath} /> 52 <SnippetContent skipPadding hideOverflow> 53 <Diff viewType="unified" diffType={type} hunks={hunks}> 54 {(hunks: any[]) => hunks.map(hunk => <Hunk key={hunk.content} hunk={hunk} />)} 55 </Diff> 56 </SnippetContent> 57 </Snippet> 58 ); 59 60 return <>{diff.map(renderFile)}</>; 61}; 62