/// import type { MouseEvent } from "react"; import { Fragment, useState } from "react"; import { hydrateIsland } from "@/client/hydrate"; import { CommitRow } from "@/client/components/CommitRow"; type CommitView = { oid: string; shortOid: string; firstLine: string; authorName: string; when: string; isMerge?: boolean; }; export type MergeExpanderProps = { owner: string; repo: string; commits: CommitView[]; }; function MergeStatusRow({ message }: { message: string }) { return ( {message} ); } export function MergeExpanderIsland({ owner, repo, commits }: MergeExpanderProps) { const [expandedByOid, setExpandedByOid] = useState>({}); const [loadingByOid, setLoadingByOid] = useState>({}); const [errorByOid, setErrorByOid] = useState>({}); const [mergeRowsByOid, setMergeRowsByOid] = useState>({}); async function toggleMerge(oid: string) { if (loadingByOid[oid]) { return; } if (expandedByOid[oid]) { setExpandedByOid((current) => ({ ...current, [oid]: false })); return; } if (mergeRowsByOid[oid]) { setExpandedByOid((current) => ({ ...current, [oid]: true })); setErrorByOid((current) => ({ ...current, [oid]: null })); return; } setLoadingByOid((current) => ({ ...current, [oid]: true })); setExpandedByOid((current) => ({ ...current, [oid]: true })); setErrorByOid((current) => ({ ...current, [oid]: null })); try { const response = await fetch(`/${owner}/${repo}/commits/fragments/${oid}?limit=20`, { headers: { Accept: "application/json" }, }); if (!response.ok) { throw new Error(`HTTP ${response.status}`); } const data = (await response.json()) as { commits?: CommitView[] }; setMergeRowsByOid((current) => ({ ...current, [oid]: data.commits || [] })); } catch (error) { setErrorByOid((current) => ({ ...current, [oid]: error instanceof Error ? error.message : "Failed to load merge commits", })); } finally { setLoadingByOid((current) => ({ ...current, [oid]: false })); } } function onMergeRowClick(oid: string) { return (event: MouseEvent) => { const target = event.target as HTMLElement; if (target.closest("a, button")) { return; } void toggleMerge(oid); }; } return ( {commits.length ? ( commits.map((commit) => { const isMerge = Boolean(commit.isMerge); const mergeOid = commit.oid; const isExpanded = Boolean(expandedByOid[mergeOid]); const isLoading = Boolean(loadingByOid[mergeOid]); const mergeRows = mergeRowsByOid[mergeOid] || []; const error = errorByOid[mergeOid]; const mergeRowClass = isMerge ? "cursor-pointer bg-accent-50/40 hover:bg-accent-50/60 dark:bg-accent-900/15 dark:hover:bg-accent-900/25" : ""; return ( {isMerge && isExpanded && isLoading ? ( ) : null} {isMerge && isExpanded && !isLoading && error ? ( ) : null} {isMerge && isExpanded && !isLoading && !error && !mergeRows.length ? ( ) : null} {isMerge && isExpanded && !isLoading && !error ? mergeRows.map((entry) => ( )) : null} ); }) ) : ( )}
OID Message Author Date
(none)
); } export function initMergeExpander() { hydrateIsland("merge-expander", MergeExpanderIsland); }