fix: stabilize history diff loading and comments

Prevents History file loading from getting stuck
Disables inline comments in History diffs
Keeps review comments available in regular diff views
This commit is contained in:
Bohdan Triapitsyn
2026-06-17 02:02:01 +03:00
parent ada6d417a0
commit 253094cb1d
3 changed files with 55 additions and 15 deletions
+29 -2
View File
@@ -610,6 +610,8 @@ export const GitView: React.FC<GitViewProps> = ({ isActive }) => {
const [expandedCommitHashes, setExpandedCommitHashes] = React.useState<Set<string>>(new Set());
const [commitFilesMap, setCommitFilesMap] = React.useState<Map<string, CommitFileEntry[]>>(new Map());
const [loadingCommitHashes, setLoadingCommitHashes] = React.useState<Set<string>>(new Set());
const commitFilesMapRef = React.useRef(commitFilesMap);
const loadingCommitHashesRef = React.useRef(loadingCommitHashes);
const [historyBranchDivider, setHistoryBranchDivider] = React.useState<HistoryBranchDivider>(null);
const [remoteUrl, setRemoteUrl] = React.useState<string | null>(null);
const [gitmojiSearch, setGitmojiSearch] = React.useState('');
@@ -725,12 +727,20 @@ export const GitView: React.FC<GitViewProps> = ({ isActive }) => {
});
}, []);
React.useEffect(() => {
commitFilesMapRef.current = commitFilesMap;
}, [commitFilesMap]);
React.useEffect(() => {
loadingCommitHashesRef.current = loadingCommitHashes;
}, [loadingCommitHashes]);
React.useEffect(() => {
if (!currentDirectory || !git) return;
// Find hashes that are expanded but not yet loaded or loading
const hashesToLoad = Array.from(expandedCommitHashes).filter(
(hash) => !commitFilesMap.has(hash) && !loadingCommitHashes.has(hash)
(hash) => !commitFilesMapRef.current.has(hash) && !loadingCommitHashesRef.current.has(hash)
);
if (hashesToLoad.length === 0) return;
@@ -742,6 +752,7 @@ export const GitView: React.FC<GitViewProps> = ({ isActive }) => {
for (const hash of hashesToLoad) {
next.add(hash);
}
loadingCommitHashesRef.current = next;
return next;
});
@@ -762,6 +773,7 @@ export const GitView: React.FC<GitViewProps> = ({ isActive }) => {
for (const { hash, files } of results) {
next.set(hash, files);
}
commitFilesMapRef.current = next;
return next;
});
setLoadingCommitHashes((prev) => {
@@ -769,14 +781,29 @@ export const GitView: React.FC<GitViewProps> = ({ isActive }) => {
for (const { hash } of results) {
next.delete(hash);
}
loadingCommitHashesRef.current = next;
return next;
});
});
return () => {
cancelled = true;
setLoadingCommitHashes((prev) => {
let changed = false;
const next = new Set(prev);
for (const hash of hashesToLoad) {
if (next.delete(hash)) {
changed = true;
}
}
if (!changed) {
return prev;
}
loadingCommitHashesRef.current = next;
return next;
});
};
}, [expandedCommitHashes, currentDirectory, git, commitFilesMap, loadingCommitHashes]);
}, [expandedCommitHashes, currentDirectory, git]);
React.useEffect(() => {
if (!currentDirectory) return;
@@ -43,6 +43,7 @@ interface PierreDiffViewerProps {
renderSideBySide: boolean;
wrapLines?: boolean;
layout?: 'fill' | 'inline';
enableComments?: boolean;
}
/**
@@ -468,6 +469,7 @@ export const PierreDiffViewer: React.FC<PierreDiffViewerProps> = ({
renderSideBySide,
wrapLines,
layout = 'fill',
enableComments = true,
}) => {
const themeContext = useOptionalThemeSystem();
@@ -528,6 +530,10 @@ export const PierreDiffViewer: React.FC<PierreDiffViewerProps> = ({
}, [commentText]);
const handleSelectionChange = useCallback((range: SelectedLineRange | null) => {
if (!enableComments) {
return;
}
// Ignore callbacks while we're programmatically applying selection
if (isApplyingSelectionRef.current) {
return;
@@ -554,7 +560,7 @@ export const PierreDiffViewer: React.FC<PierreDiffViewerProps> = ({
setCommentText('');
}
}
}, [isMobile, setCommentText, setSelection]);
}, [enableComments, isMobile, setCommentText, setSelection]);
const handleCancelComment = useCallback(() => {
cancel();
@@ -751,21 +757,25 @@ export const PierreDiffViewer: React.FC<PierreDiffViewerProps> = ({
expansionLineCount: 20,
overflow: wrapLines ? ('wrap' as const) : ('scroll' as const),
disableFileHeader: true,
enableLineSelection: true,
enableLineSelection: enableComments,
enableHoverUtility: false,
onLineSelected: handleSelectionChange,
onLineSelected: enableComments ? handleSelectionChange : undefined,
unsafeCSS: WEBKIT_SCROLL_FIX_CSS,
renderAnnotation,
}), [darkTheme.metadata.id, isDark, isLargeContent, lightTheme.metadata.id, renderSideBySide, wrapLines, handleSelectionChange, renderAnnotation]);
renderAnnotation: enableComments ? renderAnnotation : undefined,
}), [darkTheme.metadata.id, enableComments, isDark, isLargeContent, lightTheme.metadata.id, renderSideBySide, wrapLines, handleSelectionChange, renderAnnotation]);
const lineAnnotations = useMemo(() => {
if (!enableComments) {
return [];
}
return buildPierreLineAnnotations({
drafts: fileDrafts,
editingDraftId,
selection,
});
}, [editingDraftId, fileDrafts, selection]);
}, [editingDraftId, enableComments, fileDrafts, selection]);
const lineAnnotationsRef = useRef(lineAnnotations);
@@ -958,6 +968,8 @@ export const PierreDiffViewer: React.FC<PierreDiffViewerProps> = ({
}, [selection]);
useEffect(() => {
if (!enableComments) return;
const container = diffContainerRef.current;
if (!container) return;
@@ -1009,7 +1021,7 @@ export const PierreDiffViewer: React.FC<PierreDiffViewerProps> = ({
}
cleanup();
};
}, [diffThemeKey, fileName, handleSelectionChange, resolveClickedSide]);
}, [diffThemeKey, enableComments, fileName, handleSelectionChange, resolveClickedSide]);
// MutationObserver to trigger re-renders when annotation DOM nodes are added/removed
useEffect(() => {
@@ -1052,7 +1064,7 @@ export const PierreDiffViewer: React.FC<PierreDiffViewerProps> = ({
return null;
}
const commentOverlays = (
const commentOverlays = enableComments ? (
<PierreDiffCommentOverlays
diffRootRef={diffRootRef}
drafts={fileDrafts}
@@ -1073,7 +1085,7 @@ export const PierreDiffViewer: React.FC<PierreDiffViewerProps> = ({
}}
onDelete={deleteDraft}
/>
);
) : null;
if (layout === 'fill') {
return (
@@ -660,10 +660,11 @@ export const HistoryCommitRow = React.memo(({
original={cached.original}
modified={cached.modified}
language={getLanguageFromExtension(file.path) || ''}
fileName={file.path}
renderSideBySide={false}
layout="inline"
/>
fileName={file.path}
renderSideBySide={false}
layout="inline"
enableComments={false}
/>
);
})()}
</div>