import { useQuery } from '@tanstack/react-query'; import { GitBranchIcon, TagIcon } from 'lucide-react'; import { Skeleton } from '#/components/atoms/skeleton.jsx'; import { CodeMenu } from '#/components/molecules/code-menu.jsx'; import { DirTable } from '#/components/molecules/dir-table.jsx'; import { FileCommit } from '#/components/molecules/file-commit.jsx'; import { LatestCommit } from '#/components/molecules/latest-commit.jsx'; import { ListBox } from '#/components/molecules/list-box.jsx'; import { ListSkeleton } from '#/components/molecules/list-skeleton.jsx'; import { PageNotice } from '#/components/molecules/page-notice.jsx'; import { QuickSetup } from '#/components/molecules/quick-setup.jsx'; import { ReadingRepo } from '#/components/molecules/reading-repo.jsx'; import { ReadmePanel } from '#/components/molecules/readme-panel.jsx'; import { RefSelect } from '#/components/molecules/ref-select.jsx'; import { RepoAbout } from '#/components/molecules/repo-about.jsx'; import { RepoHeading } from '#/components/molecules/repo-heading.jsx'; import { Screen } from '#/components/molecules/screen.jsx'; import { SourceCrumbs } from '#/components/molecules/source-crumbs.jsx'; import { SourcePanes } from '#/components/molecules/source-panes.jsx'; import { account, branchRefs, capabilities, isEmpty, readerFor, repoRecord, repoSize, tagRefs, } from '#/lib/git.js'; import { useNavigate } from '#/lib/navigation.jsx'; import { useDirectory, useFileCommit, useHistory } from '#/lib/source.js'; const README = /^readme(\.md|\.txt|\.markdown)?$/i; export function TreePage({ repo, refName, path, space }) { const navigate = useNavigate(); const record = repoRecord(repo, space); // The listing the pane beside this one draws. Reading it here too costs // nothing: one query key serves both. const { data: entries, error } = useDirectory(repo, refName, path, space); // What this directory says about itself. A directory with no README leaves // the reading pane empty, which is what a checkout looks like. const readme = (entries ?? []).find( (entry) => entry.type === 'file' && README.test(entry.name), ); const { data: readmeFile } = useQuery({ queryKey: ['readme', repo, refName, path, readme?.name, space ?? ''], enabled: Boolean(readme), queryFn: () => readerFor(repo, undefined, space).readFile( account.did, repo, refName, path ? `${path}/${readme.name}` : readme.name, ), }); // The tip of the ref and how far its history runs. The gauges in the pane // beside this one read the same answer. const { data: history } = useHistory(repo, refName, space); // The last commit to touch this directory, for the panel's header inside // one. The walk runs behind the paint and the line fills in when it lands. const { data: dirCommit, isPending: dirCommitPending } = useFileCommit( repo, refName, path, space, ); if (!record) { return No such repository.; } const branches = branchRefs(record).length; const tags = tagRefs(record).length; // What this view is looking at: which ref, and where inside it. The // sections of the repository, and cloning it, are in the band above. // // Inside a directory the path leads, and the picker answers its right, the // way a forge reads: the reader is navigating the path. At the root there // is no path, so the picker stands first and the ref counts beside it. const selectRef = (next) => navigate(`/${encodeURIComponent(repo)}/tree/${encodeURI(next)}`); const refPicker = ( ); if (isEmpty(record)) { return (
); } const readmePanel = readme && ( ); // The root is where a repository is read about: its files in one panel // with the latest commit on the panel's header, what it says about itself // in the panel under that, and what it is beside them. Inside a directory // the reader is navigating, and the panes take over. if (!path) { return (
{/* What the panel below is looking at, and the one action a repository has: the way to take it away. */}
{refPicker} {branches} {branches === 1 ? 'branch' : 'branches'} {tags > 0 && ( {tags} {tags === 1 ? 'tag' : 'tags'} )}
{error && {error.message}} {history?.head ? ( ) : (
)} {entries ? ( ) : ( !error && ( <>
) )}
{readmePanel}
); } return (
{refPicker}} /> {error && {error.message}} {!entries && !error && ( <>
)} {entries && ( <> {readmePanel} )}
); }