authorgravatar for maria@kuuro.netmaria <maria@kuuro.net> 2026-09-04 14:51:18-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-09-04 14:51:18-04:00
loga76b898b3aa4fd4081104b06a3b2c942bd4fecb7
tree2cf08151632f6a81d89a51973001156ce156c6c7
parentcf9729d5ee9660c08556e823080d3bb19648ed28
signature Signed by PGP key B5690EEEBB952194

feat(web): link pull request authors to profiles (#9627)

Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com>

2 files changed, 39 insertions(+), 17 deletions(-)

apps/web/src/components/pullRequest/PullRequestDetailPanel.tsx+18-15
......@@ -141,7 +141,6 @@ import {
141141} from "./pullRequestProjectAssignment.logic";
142142import { PullRequestChecksPopover } from "./PullRequestChecksPopover";
143143import {
144 PullRequestActorAvatar,
145144 PullRequestActorLabel,
146145 PullRequestDiffStat,
147146 PullRequestMetaLine,
......@@ -656,6 +655,13 @@ export function PullRequestDetailPanel({
656655 if (detail?.autoMergeMethod !== undefined) setMergeMethod(detail.autoMergeMethod);
657656 }, [detail?.autoMergeMethod, pullRequestKey]);
658657 const repositoryUrl = detail === null ? null : changeRequestRepositoryUrl(detail.url);
658 const authorProfileUrl =
659 detail?.provider === "github" &&
660 detail.author !== null &&
661 !detail.author.login.endsWith("[bot]") &&
662 repositoryUrl !== null
663 ? new URL(`/${encodeURIComponent(detail.author.login)}`, repositoryUrl).toString()
664 : null;
659665 const checkoutCommand = detail
660666 ? pullRequestCheckoutCommand(
661667 detail.provider,
......@@ -1870,19 +1876,12 @@ export function PullRequestDetailPanel({
18701876 <div className="col-span-2 min-w-0 px-4 pb-2 pt-1">
18711877 <div className="flex min-w-0 items-center gap-2 text-xs text-muted-foreground">
18721878 <span className="flex min-w-0 shrink items-center gap-1.5 overflow-hidden text-xs text-muted-foreground">
1873 <Tooltip>
1874 <TooltipTrigger
1875 render={
1876 <span
1877 className="shrink-0 rounded-full"
1878 aria-label={detail.author?.login ?? "ghost"}
1879 />
1880 }
1881 >
1882 <PullRequestActorAvatar actor={detail.author} />
1883 </TooltipTrigger>
1884 <TooltipPopup side="top">{detail.author?.login ?? "ghost"}</TooltipPopup>
1885 </Tooltip>
1879 <PullRequestActorLabel
1880 actor={detail.author}
1881 profileUrl={authorProfileUrl}
1882 className="shrink-0 rounded-full"
1883 labelClassName="sr-only"
1884 />
18861885 <span className="shrink-0">{formatRelativeTimeLabel(detail.updatedAt)}</span>
18871886 </span>
18881887 <span aria-hidden className="h-3 w-px shrink-0 bg-border/70" />
......@@ -2042,7 +2041,11 @@ export function PullRequestDetailPanel({
20422041 )}
20432042 <div className="mt-2 flex min-w-0 items-center gap-2 text-xs text-muted-foreground">
20442043 <PullRequestMetaLine className="min-w-0 whitespace-nowrap">
2045 <PullRequestActorLabel actor={detail.author} className="font-medium" />
2044 <PullRequestActorLabel
2045 actor={detail.author}
2046 profileUrl={authorProfileUrl}
2047 className="font-medium"
2048 />
20462049 <span>updated {formatRelativeTimeLabel(detail.updatedAt)}</span>
20472050 </PullRequestMetaLine>
20482051 {checkoutCommand ? (
apps/web/src/components/pullRequest/pullRequestPresentation.tsx+21-2
......@@ -358,11 +358,13 @@ export function PullRequestActorLabel({
358358 className,
359359 labelClassName,
360360 tooltip = true,
361 profileUrl,
361362}: {
362363 actor: PullRequestActor | null;
363364 className?: string;
364365 labelClassName?: string;
365366 tooltip?: boolean;
367 profileUrl?: string | null;
366368}) {
367369 const login = actor?.login ?? "ghost";
368370 const label = (
......@@ -377,11 +379,28 @@ export function PullRequestActorLabel({
377379 return (
378380 <Tooltip>
379381 <TooltipTrigger
380 render={<span className={cn("flex min-w-0 items-center gap-1.5", className)} />}
382 render={
383 profileUrl ? (
384 <a
385 href={profileUrl}
386 target="_blank"
387 rel="noopener noreferrer"
388 aria-label={`Open ${login}'s profile`}
389 />
390 ) : (
391 <span />
392 )
393 }
394 className={cn(
395 "flex min-w-0 items-center gap-1.5",
396 profileUrl &&
397 "cursor-pointer rounded-sm underline-offset-2 outline-none hover:text-foreground hover:underline focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-background",
398 className,
399 )}
381400 >
382401 {label}
383402 </TooltipTrigger>
384 <TooltipPopup side="top">{login}</TooltipPopup>
403 <TooltipPopup side="top">{profileUrl ? `Open ${login}'s profile` : login}</TooltipPopup>
385404 </Tooltip>
386405 );
387406}