diff --git a/src/components/PatchActivityItem.svelte b/src/components/PatchActivityItem.svelte index 47a134590..a49057c1f 100644 --- a/src/components/PatchActivityItem.svelte +++ b/src/components/PatchActivityItem.svelte @@ -257,6 +257,11 @@ .merge-badge :global(*) { color: inherit; } + .merge-target { + display: inline-flex; + align-items: center; + gap: 0.25rem; + } .copy-link { display: inline-flex; align-items: center; @@ -572,7 +577,11 @@
merged patch {#if targetBranch} - into {targetBranch} + into + + + {targetBranch} + {/if}
diff --git a/src/components/PatchMetadata.svelte b/src/components/PatchMetadata.svelte index 0e2a5c6a7..9c6676f70 100644 --- a/src/components/PatchMetadata.svelte +++ b/src/components/PatchMetadata.svelte @@ -6,6 +6,7 @@ import type { Config } from "@bindings/config/Config"; import type { Stats } from "@bindings/diff/Stats"; import type { RepoInfo } from "@bindings/repo/RepoInfo"; + import type { RepoRefs } from "@bindings/repo/RepoRefs"; import debounce from "lodash/debounce"; @@ -19,12 +20,14 @@ summaryTitle, } from "@app/lib/reviewSummary"; import * as roles from "@app/lib/roles"; - import { push } from "@app/lib/router"; + import { push, routeToPath } from "@app/lib/router"; import { authorForNodeId, + defaultBranch, formatOid, pluralize, publicKeyFromDid, + unqualifyBranch, verdictIcon, } from "@app/lib/utils"; @@ -149,6 +152,58 @@ await loadPatch(); } } + + const targetBranch = $derived( + patch.targetBranch === undefined + ? undefined + : unqualifyBranch(patch.targetBranch), + ); + const targetBranchCaption = $derived( + `This patch merges into ${targetBranch}`, + ); + + // A patch's target is not validated when it is opened, so it can name a + // branch the source view cannot resolve. The default branch is canonical by + // definition; any other target needs the canonical ref list, which is + // expensive enough to load after render. An unresolved target renders + // unlinked — a missing link beats one that errors. + const targetIsDefaultBranch = $derived( + targetBranch !== undefined && targetBranch === defaultBranch(repo), + ); + + let canonicalBranches: Record | undefined = $state(); + let canonicalBranchesRid: string | undefined; + + $effect(() => { + if (targetBranch === undefined || targetIsDefaultBranch) { + return; + } + const requested = repo.rid; + if (canonicalBranchesRid === requested) { + return; + } + canonicalBranchesRid = requested; + canonicalBranches = undefined; + void invoke("list_repo_refs", { rid: requested }) + .then(refs => { + if (canonicalBranchesRid === requested) { + canonicalBranches = refs.canonical.branches; + } + }) + .catch(error => { + if (canonicalBranchesRid === requested) { + canonicalBranchesRid = undefined; + } + console.error("Failed to load repo refs", error); + }); + }); + + const targetBranchBrowsable = $derived( + targetIsDefaultBranch || + (targetBranch !== undefined && + canonicalBranches !== undefined && + targetBranch in canonicalBranches), + );