113 lines
3.5 KiB
TypeScript
113 lines
3.5 KiB
TypeScript
import type { DiffFile } from '../types';
|
||
import { ConfirmDialog } from './ConfirmDialog';
|
||
import { Icon } from './Icon';
|
||
|
||
// A diff the server refused to hand over unasked, because rendering it would
|
||
// wedge the page (see git.Repo.Diff). Two shapes, both built from the file
|
||
// summary that came back in its place:
|
||
//
|
||
// - OversizeWarning — the modal that asks, put up as soon as the diff lands.
|
||
// - OversizeNotice — what stands in for the diff afterwards, so a dismissed
|
||
// warning doesn't leave an empty screen with no way back.
|
||
//
|
||
// The size is nearly always a base ref whose history has moved on rather than a
|
||
// genuinely enormous review, so both of them point at the base and at what to
|
||
// switch to.
|
||
|
||
const n = (x: number) => x.toLocaleString();
|
||
|
||
// sizeLine describes the change set in one phrase: "412 files, 87,204 changed
|
||
// lines". Binary files count for no lines, so the file count carries them.
|
||
function sizeLine(files: DiffFile[]): string {
|
||
const lines = files.reduce((total, f) => total + f.additions + f.deletions, 0);
|
||
return `${n(files.length)} file${files.length === 1 ? '' : 's'}, ${n(lines)} changed line${
|
||
lines === 1 ? '' : 's'
|
||
}`;
|
||
}
|
||
|
||
// commitAdvice points at the other way through a change set too big to render:
|
||
// the commits are listed in the left rail whether or not the patch loaded, and
|
||
// one of them at a time costs nothing.
|
||
function commitAdvice(commits: number): string | null {
|
||
if (commits === 0) return null;
|
||
return (
|
||
'The commits it spans are listed in the left rail — reading one at a time ' +
|
||
'renders only that commit, however big the whole range is.'
|
||
);
|
||
}
|
||
|
||
// advice suggests the way out, which depends on what the base already is.
|
||
function advice(base: string, suggested: string): string {
|
||
if (base === 'HEAD') {
|
||
return 'That is a lot of uncommitted work for one screen.';
|
||
}
|
||
const alternative = suggested && suggested !== base ? `${suggested}, or HEAD` : 'HEAD';
|
||
return (
|
||
`A diff this size usually means ${base} has moved on since this work was ` +
|
||
`cut from it, so the change set is padded with commits nobody is reviewing. ` +
|
||
`Switching the base to ${alternative} will show only the work itself.`
|
||
);
|
||
}
|
||
|
||
export function OversizeWarning({
|
||
files,
|
||
base,
|
||
suggested,
|
||
commits,
|
||
onLoad,
|
||
onCancel,
|
||
}: {
|
||
files: DiffFile[];
|
||
base: string;
|
||
suggested: string;
|
||
commits: number;
|
||
onLoad: () => void;
|
||
onCancel: () => void;
|
||
}) {
|
||
return (
|
||
<ConfirmDialog
|
||
title="This diff is very large"
|
||
confirmLabel="Load it anyway"
|
||
onConfirm={onLoad}
|
||
onCancel={onCancel}
|
||
>
|
||
<p>
|
||
<code>{base}</code> gives <strong>{sizeLine(files)}</strong>. Rendering
|
||
that much at once can leave the page unresponsive for a while.
|
||
</p>
|
||
<p>{advice(base, suggested)}</p>
|
||
{commitAdvice(commits) && <p>{commitAdvice(commits)}</p>}
|
||
</ConfirmDialog>
|
||
);
|
||
}
|
||
|
||
export function OversizeNotice({
|
||
files,
|
||
base,
|
||
suggested,
|
||
commits,
|
||
onLoad,
|
||
}: {
|
||
files: DiffFile[];
|
||
base: string;
|
||
suggested: string;
|
||
commits: number;
|
||
onLoad: () => void;
|
||
}) {
|
||
return (
|
||
<div className="oversize-notice">
|
||
<Icon name="alert" size={20} />
|
||
<h2>Diff not loaded</h2>
|
||
<p>
|
||
<code>{base}</code> gives {sizeLine(files)} — enough to make the page
|
||
unresponsive, so it wasn’t rendered.
|
||
</p>
|
||
<p>{advice(base, suggested)}</p>
|
||
{commitAdvice(commits) && <p>{commitAdvice(commits)}</p>}
|
||
<button className="btn-ghost" onClick={onLoad}>
|
||
Load it anyway
|
||
</button>
|
||
</div>
|
||
);
|
||
}
|