/** * @file One file's history (Show History), like GitLens' File History: every * commit that touched it, newest first, followed across renames (`git log * --follow`). A version opens that commit's diff of the file under the name * it had there (the panel's own diff viewer); Blame opens the file as it was * at that version with each line's commit (BlameView), and a rename says * where it came from. Paged with Load more. * @author Nguyễn Ngọc Trí Vĩ */ import { useCallback, useEffect, useRef, useState } from "react"; import { useTranslation } from "react-i18next"; import { api, type GitTarget } from "../../../lib/api"; import type { FileVersion } from "../../../lib/gitHistory"; import { timeAgo } from "../../../lib/format"; import { BlameView } from "./BlameView"; const PAGE = 100; export function FileHistory({ target, path, reread, onOpenVersion, onViewCommit, onWholeBranch, }: { target: GitTarget; path: string; reread: number; /** That version's diff of the file: its name THEN, and the commit. */ onOpenVersion: (path: string, sha: string) => void; onViewCommit: (sha: string) => void; onWholeBranch: () => void; }) { const { t } = useTranslation(["gitHistory", "lanes"]); const [versions, setVersions] = useState(null); const [hasMore, setHasMore] = useState(false); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const [blame, setBlame] = useState<{ path: string; rev: string } | null>(null); const seq = useRef(0); useEffect(() => { const mine = ++seq.current; setVersions(null); setError(null); setLoading(true); void api.lanes.gitHistory .fileHistory(target, path, { limit: PAGE }) .then((r) => { if (seq.current !== mine) return; setVersions(r.available ? r.commits : []); setHasMore(r.available && r.hasMore); }) .catch( (err) => seq.current === mine && setError(err instanceof Error ? err.message : String(err)) ) .finally(() => seq.current === mine && setLoading(false)); // eslint-disable-next-line react-hooks/exhaustive-deps -- target is stable per panel }, [path, reread]); const more = useCallback(() => { if (!versions || loading) return; const mine = ++seq.current; setLoading(true); void api.lanes.gitHistory .fileHistory(target, path, { limit: PAGE, skip: versions.length }) .then((r) => { if (seq.current !== mine || !r.available) return; setVersions((v) => [...(v ?? []), ...r.commits]); setHasMore(r.hasMore); }) .catch( (err) => seq.current === mine && setError(err instanceof Error ? err.message : String(err)) ) .finally(() => seq.current === mine && setLoading(false)); }, [versions, loading, target, path]); return (
{t("lanes:changes.historyOfFile", { path })}
{error && (

{error}

)} {versions === null && !error && (

{t("lanes:changes.loading")}

)} {versions && versions.length === 0 && (

{t("lanes:changes.noCommits")}

)}
    {versions?.map((v) => (
  • {v.status !== "D" && ( )}
  • ))}
{hasMore && ( )} {blame && ( setBlame(null)} onOpenCommit={(sha) => { setBlame(null); onViewCommit(sha); }} /> )}
); }