authorgravatar for 97427849+flamboh@users.noreply.github.comoliver <97427849+flamboh@users.noreply.github.com> 2026-09-04 00:57:35-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-09-04 03:57:35-04:00
log706231535ceac8618712913dfdc4a058c2ffb0d8
tree69b14e0d379ad536a6a18d58f2cb86d96f6478ef
parent93c3ab4ffe408a3e06228a33efc8a0745da91178
signature Signed by PGP key B5690EEEBB952194

fix(web): match composer pull request state icons (#9375)


4 files changed, 43 insertions(+), 8 deletions(-)

apps/web/src/components/BranchToolbarBranchSelector.tsx+5-1
...@@ -745,7 +745,11 @@ export function BranchToolbarBranchSelector({...@@ -745,7 +745,11 @@ export function BranchToolbarBranchSelector({
745 />745 />
746 }746 }
747 >747 >
748 <ChangeRequestStatusIcon className="size-3" />748 <ChangeRequestStatusIcon
749 state={branchPr.state}
750 isDraft={branchPr.isDraft}
751 className="size-3"
752 />
749 <span753 <span
750 data-composer-label754 data-composer-label
751 className="min-w-0 max-w-12 overflow-hidden group-data-[compact]/composer-context:max-w-0"755 className="min-w-0 max-w-12 overflow-hidden group-data-[compact]/composer-context:max-w-0"
apps/web/src/components/LegacySidebar.tsx+6-2
...@@ -720,7 +720,7 @@ export const SidebarThreadRow = memo(function SidebarThreadRow(props: SidebarThr...@@ -720,7 +720,7 @@ export const SidebarThreadRow = memo(function SidebarThreadRow(props: SidebarThr
720 onContextMenu={handleRowContextMenu}720 onContextMenu={handleRowContextMenu}
721 >721 >
722 <div className="flex min-w-0 flex-1 items-center gap-1.5 text-left">722 <div className="flex min-w-0 flex-1 items-center gap-1.5 text-left">
723 {prStatus && (723 {prStatus && pr && (
724 <Tooltip>724 <Tooltip>
725 <TooltipTrigger725 <TooltipTrigger
726 render={726 render={
...@@ -733,7 +733,11 @@ export const SidebarThreadRow = memo(function SidebarThreadRow(props: SidebarThr...@@ -733,7 +733,11 @@ export const SidebarThreadRow = memo(function SidebarThreadRow(props: SidebarThr
733 onPointerDown={(event) => event.stopPropagation()}733 onPointerDown={(event) => event.stopPropagation()}
734 onClick={handlePrClick}734 onClick={handlePrClick}
735 >735 >
736 <ChangeRequestStatusIcon className="size-3" />736 <ChangeRequestStatusIcon
737 state={pr.state}
738 isDraft={pr.isDraft}
739 className="size-3"
740 />
737 </a>741 </a>
738 }742 }
739 />743 />
apps/web/src/components/ThreadStatusIndicators.test.ts+18
...@@ -2,8 +2,15 @@ import { ProjectId, type PullRequestSummary, type VcsStatusResult } from "@t3too...@@ -2,8 +2,15 @@ import { ProjectId, type PullRequestSummary, type VcsStatusResult } from "@t3too
2import { describe, expect, it } from "@effect/vitest";2import { describe, expect, it } from "@effect/vitest";
3import * as Effect from "effect/Effect";3import * as Effect from "effect/Effect";
4import { AtomRegistry } from "effect/unstable/reactivity";4import { AtomRegistry } from "effect/unstable/reactivity";
5import {
6 GitMergeIcon,
7 GitPullRequestClosedIcon,
8 GitPullRequestDraftIcon,
9 GitPullRequestIcon,
10} from "lucide-react";
511
6import {12import {
13 ChangeRequestStatusIcon,
7 nextThreadChangeRequestSnapshot,14 nextThreadChangeRequestSnapshot,
8 prStatusIndicator,15 prStatusIndicator,
9 resolveDisplayedThreadPr,16 resolveDisplayedThreadPr,
...@@ -16,6 +23,17 @@ import {...@@ -16,6 +23,17 @@ import {
16} from "./ThreadStatusIndicators";23} from "./ThreadStatusIndicators";
17import { newestPullRequestSummary } from "../state/pullRequests";24import { newestPullRequestSummary } from "../state/pullRequests";
1825
26describe("ChangeRequestStatusIcon", () => {
27 it.each([
28 ["open", "open", false, GitPullRequestIcon],
29 ["draft", "open", true, GitPullRequestDraftIcon],
30 ["closed", "closed", false, GitPullRequestClosedIcon],
31 ["merged", "merged", false, GitMergeIcon],
32 ] as const)("uses the %s pull request glyph", (_label, state, isDraft, expectedIcon) => {
33 expect(ChangeRequestStatusIcon({ state, isDraft }).type).toBe(expectedIcon);
34 });
35});
36
19function status(overrides: Partial<VcsStatusResult> = {}): VcsStatusResult {37function status(overrides: Partial<VcsStatusResult> = {}): VcsStatusResult {
20 return {38 return {
21 isRepo: true,39 isRepo: true,
apps/web/src/components/ThreadStatusIndicators.tsx+14-5
...@@ -11,7 +11,7 @@ import {...@@ -11,7 +11,7 @@ import {
11 type VcsStatusResult,11 type VcsStatusResult,
12} from "@t3tools/contracts";12} from "@t3tools/contracts";
13import { Atom } from "effect/unstable/reactivity";13import { Atom } from "effect/unstable/reactivity";
14import { FolderGit2Icon, GitPullRequestIcon, TerminalIcon } from "lucide-react";14import { FolderGit2Icon, TerminalIcon } from "lucide-react";
15import { useMemo } from "react";15import { useMemo } from "react";
16import { appAtomRegistry } from "../rpc/atomRegistry";16import { appAtomRegistry } from "../rpc/atomRegistry";
17import { useEnvironment, usePrimaryEnvironmentId } from "../state/environments";17import { useEnvironment, usePrimaryEnvironmentId } from "../state/environments";
...@@ -24,6 +24,7 @@ import { vcsEnvironment } from "../state/vcs";...@@ -24,6 +24,7 @@ import { vcsEnvironment } from "../state/vcs";
24import { useUiStateStore } from "../uiStateStore";24import { useUiStateStore } from "../uiStateStore";
25import { resolveChangeRequestPresentation } from "../sourceControlPresentation";25import { resolveChangeRequestPresentation } from "../sourceControlPresentation";
26import { resolveThreadStatusPill, type ThreadStatusPill } from "./Sidebar.logic";26import { resolveThreadStatusPill, type ThreadStatusPill } from "./Sidebar.logic";
27import { resolvePullRequestState } from "./pullRequest/pullRequestPresentation";
27import type { SidebarThreadSummary } from "../types";28import type { SidebarThreadSummary } from "../types";
28import { formatWorktreePathForDisplay } from "../worktreeCleanup";29import { formatWorktreePathForDisplay } from "../worktreeCleanup";
29import { Tooltip, TooltipPopup, TooltipTrigger } from "./ui/tooltip";30import { Tooltip, TooltipPopup, TooltipTrigger } from "./ui/tooltip";
...@@ -155,8 +156,16 @@ export function prStatusIndicator(...@@ -155,8 +156,16 @@ export function prStatusIndicator(
155 return null;156 return null;
156}157}
157158
158export function ChangeRequestStatusIcon({ className }: { className?: string }) {159export function ChangeRequestStatusIcon({
159 return <GitPullRequestIcon className={className} />;160 state,
161 isDraft = false,
162 className,
163}: Pick<NonNullable<ThreadPr>, "state"> & {
164 readonly isDraft?: boolean | undefined;
165 readonly className?: string | undefined;
166}) {
167 const presentation = resolvePullRequestState({ state, isDraft });
168 return <presentation.Icon className={className} />;
160}169}
161170
162export function PrStatusTooltipContent({ status }: { status: PrStatusIndicator }) {171export function PrStatusTooltipContent({ status }: { status: PrStatusIndicator }) {
...@@ -585,7 +594,7 @@ export function ThreadRowLeadingStatus({ thread }: { thread: SidebarThreadSummar...@@ -585,7 +594,7 @@ export function ThreadRowLeadingStatus({ thread }: { thread: SidebarThreadSummar
585594
586 return (595 return (
587 <span className="inline-flex shrink-0 items-center gap-1.5">596 <span className="inline-flex shrink-0 items-center gap-1.5">
588 {prStatus ? (597 {prStatus && pr ? (
589 <Tooltip>598 <Tooltip>
590 <TooltipTrigger599 <TooltipTrigger
591 render={600 render={
...@@ -595,7 +604,7 @@ export function ThreadRowLeadingStatus({ thread }: { thread: SidebarThreadSummar...@@ -595,7 +604,7 @@ export function ThreadRowLeadingStatus({ thread }: { thread: SidebarThreadSummar
595 />604 />
596 }605 }
597 >606 >
598 <ChangeRequestStatusIcon className="size-3" />607 <ChangeRequestStatusIcon state={pr.state} isDraft={pr.isDraft} className="size-3" />
599 </TooltipTrigger>608 </TooltipTrigger>
600 <TooltipPopup side="top">609 <TooltipPopup side="top">
601 <PrStatusTooltipContent status={prStatus} />610 <PrStatusTooltipContent status={prStatus} />