Files
playpen/web/src/components/Oversize.tsx
T

113 lines
3.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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 wasnt rendered.
</p>
<p>{advice(base, suggested)}</p>
{commitAdvice(commits) && <p>{commitAdvice(commits)}</p>}
<button className="btn-ghost" onClick={onLoad}>
Load it anyway
</button>
</div>
);
}