diff --git a/server/app/query/create/page.tsx b/server/app/query/create/page.tsx index 3af53ff..490542f 100644 --- a/server/app/query/create/page.tsx +++ b/server/app/query/create/page.tsx @@ -1,8 +1,12 @@ "use client"; -import { useState, FormEvent, useEffect, Fragment } from "react"; +import { useState, useRef, FormEvent, useEffect, Fragment } from "react"; import clsx from "clsx"; -import { Listbox, Transition } from "@headlessui/react"; -import { CheckIcon, ChevronUpDownIcon } from "@heroicons/react/20/solid"; +import { Listbox, Transition, Combobox } from "@headlessui/react"; +import { + CheckIcon, + ChevronUpDownIcon, + ArrowPathIcon, +} from "@heroicons/react/20/solid"; import { useRouter } from "next/navigation"; import { ExclamationCircleIcon } from "@heroicons/react/20/solid"; import QueryStartedAlert from "@/app/alert"; @@ -12,7 +16,7 @@ import { RemoteServersType, } from "@/app/query/servers"; import NewQueryId from "@/app/query/haikunator"; -import { Branch, Branches, isValidCommitHash } from "@/app/query/github"; +import { Branch, Branches, Commits } from "@/app/query/github"; export default function Page() { const [queryId, setQueryId] = useState(null); @@ -110,24 +114,32 @@ function IPAForm({ }: { handleIPAFormSubmit: (event: FormEvent) => void; }) { + enum CommitSpecifier { + COMMIT_HASH, + BRANCH, + } const owner = "private-attribution"; const repo = "ipa"; const [branches, setBranches] = useState([]); + const [commitHashes, setCommitHashes] = useState([]); const branchNames = branches.map((branch) => branch.name); const [selectedBranchName, setSelectedBranchName] = useState("main"); const [selectedCommitHash, setSelectedCommitHash] = useState(""); const [validCommitHash, setValidCommitHash] = useState(true); - - enum CommitSpecifier { - COMMIT_HASH, - BRANCH, - } + const commitHashInputRef = useRef(null); const [commitSpecifier, setCommitSpecifier] = useState( CommitSpecifier.BRANCH, ); - const disableBranch = commitSpecifier != CommitSpecifier.BRANCH; const disableCommitHash = commitSpecifier != CommitSpecifier.COMMIT_HASH; + const filteredCommitHashes = + selectedCommitHash === "" + ? [] + : commitHashes.filter((commit) => { + return commit + .toLowerCase() + .startsWith(selectedCommitHash.toLowerCase()); + }); useEffect(() => { const branch = branches.find( @@ -146,29 +158,48 @@ function IPAForm({ useEffect(() => { const branch = branches.find( - (branch) => branch.name === selectedCommitHash, + (branch) => branch.commitHash === selectedCommitHash, ); + const fetchCommitIsValid = async () => { - const _valid = await isValidCommitHash(owner, repo, selectedCommitHash); + const _valid = filteredCommitHashes.length > 0; setValidCommitHash(_valid); }; if (branch) { setSelectedBranchName(branch.name); setValidCommitHash(true); } else if (commitSpecifier != CommitSpecifier.BRANCH) { - setSelectedBranchName("N/A"); + setSelectedBranchName("[Specific commit]"); fetchCommitIsValid().catch(console.error); } - }, [selectedCommitHash, commitSpecifier, CommitSpecifier.COMMIT_HASH]); + }, [ + selectedCommitHash, + commitSpecifier, + CommitSpecifier.BRANCH, + branches, + commitHashes, + ]); useEffect(() => { const fetchBranches = async () => { - const _branches = await Branches(owner, repo); + const _branches = await Branches(owner, repo, false); setBranches(_branches); }; + const fetchCommitHashes = async () => { + const _commitHashes = await Commits(owner, repo, false); + setCommitHashes(_commitHashes); + }; fetchBranches().catch(console.error); + fetchCommitHashes().catch(console.error); }, []); + const refreshBranches = async (selectedCommitHash: string) => { + const _branches = await Branches(owner, repo, true); + setBranches(_branches); + const _commitHashes = await Commits(owner, repo, true); + setCommitHashes(_commitHashes); + }; + return (
IPA Query -
setCommitSpecifier(CommitSpecifier.BRANCH)}> - +
+
{ + setCommitSpecifier(CommitSpecifier.BRANCH); + }} + > + +
+
setCommitSpecifier(CommitSpecifier.COMMIT_HASH)} + onClick={() => { + setCommitSpecifier(CommitSpecifier.COMMIT_HASH); + if (commitHashInputRef.current) { + commitHashInputRef.current.select(); + } + }} > - setSelectedCommitHash(e.target.value)} - aria-invalid="true" - aria-describedby="email-error" - /> + + setSelectedCommitHash(event.target.value)} + type="string" + name="commit_hash" + id="commit_hash" + ref={commitHashInputRef} + className={clsx( + "block w-full rounded-md border-0 py-1.5 pl-3 text-gray-900 ring-1 ring-inset focus:ring-2 focus:ring-inset sm:text-sm sm:leading-6", + !validCommitHash && + "text-red-900 ring-red-300 placeholder:text-red-300 focus:ring-red-500", + disableCommitHash && "opacity-25", + )} + /> + + {filteredCommitHashes.map((commit) => ( + + {({ active, selected }) => ( +
  • + {commit} +
  • + )} +
    + ))} +
    +
    {!validCommitHash && (
    @@ -343,15 +411,28 @@ function PassedStateSelectMenu({ disabled && "opacity-25", )} > - - {selected} - - - + {disabled ? ( + // Listbox.Button overrides the onClick, but we only need that to reactivate. +
    + {selected} + + +
    + ) : ( + + {selected} + + + + )} { +const octokit = new Octokit({ + userAgent: "draft/v0.0.1", + auth: process.env.OCTOKIT_GITHUB_API_KEY, +}); + +export async function Branches( + owner: string, + repo: string, + bypassCache: boolean, +): Promise { + const requestParams: any = { + owner: owner, + repo: repo, + per_page: 100, + request: { + cache: bypassCache ? "reload" : "default", + }, + timestamp: new Date().getTime(), + }; const branchesIter = octokit.paginate.iterator( octokit.rest.repos.listBranches, - { - owner: owner, - repo: repo, - per_page: 100, - auth: process.env.OCTOKIT_GITHUB_API_KEY, - }, + requestParams, ); let branchesArray: Branch[] = []; @@ -32,30 +43,36 @@ export async function Branches(owner: string, repo: string): Promise { for (const branch of branches) { branchesArray.push({ name: branch.name, - commitHash: branch.commit.sha, + commitHash: branch.commit.sha.substring(0, 7), }); } } - const mainBranchIndex = branchesArray.findIndex( (branch) => branch.name === "main", ); if (mainBranchIndex != -1) { branchesArray.unshift(branchesArray.splice(mainBranchIndex, 1)[0]); } - branchesArray.unshift({ name: "N/A", commitHash: "" }); return branchesArray; } -export async function Commits(owner: string, repo: string): Promise { +export async function Commits( + owner: string, + repo: string, + bypassCache: boolean, +): Promise { + const requestParams: any = { + owner: owner, + repo: repo, + per_page: 100, + request: { + cache: bypassCache ? "reload" : "default", + }, + timestamp: new Date().getTime(), + }; const commitsIter = octokit.paginate.iterator( octokit.rest.repos.listCommits, - { - owner: owner, - repo: repo, - per_page: 100, - auth: process.env.OCTOKIT_GITHUB_API_KEY, - }, + requestParams, ); let commitsArray: string[] = []; @@ -66,12 +83,3 @@ export async function Commits(owner: string, repo: string): Promise { } return commitsArray; } - -export async function isValidCommitHash( - owner: string, - repo: string, - commitHash: string, -): Promise { - const commits = await Commits(owner, repo); - return commits.includes(commitHash); -}