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