diff --git a/apps/mobile/src/features/threads/thread-list-items.tsx b/apps/mobile/src/features/threads/thread-list-items.tsx index 0ec50c674451..a4da32ef7018 100644 --- a/apps/mobile/src/features/threads/thread-list-items.tsx +++ b/apps/mobile/src/features/threads/thread-list-items.tsx @@ -44,11 +44,11 @@ export const THREAD_LIST_COMPACT_INSET = HOME_HORIZONTAL_INSET; const SIDEBAR_ROW_RADIUS = 12; function pullRequestTintColor( - pr: Pick, + pr: Pick, colorScheme: "light" | "dark", ) { const dark = colorScheme === "dark"; - if (pr.state === "open" && pr.isDraft === true) { + if (pr.others > 0 || (pr.state === "open" && pr.isDraft === true)) { return dark ? "#a1a1aa" : "#71717a"; } switch (pr.state) { diff --git a/apps/mobile/src/features/threads/thread-list-v2-items.tsx b/apps/mobile/src/features/threads/thread-list-v2-items.tsx index 67a917c0079c..937ca5f909a8 100644 --- a/apps/mobile/src/features/threads/thread-list-v2-items.tsx +++ b/apps/mobile/src/features/threads/thread-list-v2-items.tsx @@ -880,13 +880,7 @@ export const ThreadListV2Row = memo(function ThreadListV2Row(props: { ? materialYouStyleLayoutActive ? "accent-thread-selected-foreground" : "accent-user-bubble-foreground" - : pr.kind === "stack" || pr.isDraft || pr.state === null - ? "accent-foreground-muted" - : pr.state === "open" - ? "accent-adaptive-emerald-600-400" - : pr.state === "merged" - ? "accent-adaptive-violet-600-400" - : "accent-foreground-muted" + : "accent-foreground-muted" } /> ) : null} diff --git a/apps/mobile/src/state/thread-pr-presentation.ts b/apps/mobile/src/state/thread-pr-presentation.ts index 48fe3abf5f66..fc310d070acc 100644 --- a/apps/mobile/src/state/thread-pr-presentation.ts +++ b/apps/mobile/src/state/thread-pr-presentation.ts @@ -65,11 +65,12 @@ export function presentThreadLinkedPullRequests( const snapshot = link.snapshot; const state = badge.kind === "stack" ? badge.state : (snapshot?.state ?? null); const isDraft = snapshot?.isDraft === true && state === "open"; + const linkedCount = badge.kind === "pull-request" && badge.others > 0 ? badge.others + 1 : null; const label = badge.kind === "stack" ? String(badge.layers) - : badge.others > 0 - ? `+${badge.others}` + : linkedCount !== null + ? `+${linkedCount}` : String(link.number); return { kind: badge.kind, @@ -84,7 +85,10 @@ export function presentThreadLinkedPullRequests( badge.kind === "stack" ? `${badge.layers} pull requests in stack, ${state ?? "status pending"}` : `#${link.number} pull request ${state === null ? "status pending" : isDraft ? "draft" : state}${badge.others > 0 ? `, ${badge.others} more linked` : ""}`, - textClassName: state === null || isDraft ? "text-foreground-muted" : PR_STATE_TEXT_CLASS[state], + textClassName: + linkedCount !== null || state === null || isDraft + ? "text-foreground-muted" + : PR_STATE_TEXT_CLASS[state], }; } diff --git a/apps/mobile/src/state/use-thread-pr.test.ts b/apps/mobile/src/state/use-thread-pr.test.ts index 5fdb90e19c65..e36bd7d81434 100644 --- a/apps/mobile/src/state/use-thread-pr.test.ts +++ b/apps/mobile/src/state/use-thread-pr.test.ts @@ -87,7 +87,9 @@ describe("presentThreadLinkedPullRequests", () => { it("counts unrelated links without labelling them a stack", () => { expect(presentThreadLinkedPullRequests([linkedPr(1), linkedPr(2)])).toMatchObject({ kind: "pull-request", - label: "+1", + label: "+2", + others: 1, + textClassName: "text-foreground-muted", }); }); diff --git a/apps/web/src/components/Sidebar.tsx b/apps/web/src/components/Sidebar.tsx index 72a119cb76ba..b1583a346b56 100644 --- a/apps/web/src/components/Sidebar.tsx +++ b/apps/web/src/components/Sidebar.tsx @@ -1387,6 +1387,7 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: { // content; surface is reserved for interaction (hover, multi-select, route). const rowSurfaceClassName = cn( "group/sidebar-row relative w-full cursor-pointer overflow-hidden rounded-md text-left outline-none select-none", + variantAction === "unsettle" && "[&:not(:hover):not(:focus-within)_*]:text-secondary-label/70", props.isActive ? "bg-sidebar-row-active text-sidebar-foreground" : isSelected @@ -1469,7 +1470,7 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: { : "text-foreground/90", ) : cn( - "truncate group-hover/sidebar-row:text-foreground", + "truncate group-focus-within/sidebar-row:text-foreground group-hover/sidebar-row:text-foreground", shouldRecede ? "text-secondary-label/70" : props.isActive || isWoke @@ -1485,7 +1486,7 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: { ); - // Stacks show their layer count; unrelated links show only the remainder count. + // Stacks show their layer count; multiple unrelated links show their total count. // Plain clicks open T3; individual PR links also support opening the host in a new tab. const prBadgeShape = supportsMultiplePullRequests ? resolveThreadPullRequestBadge(thread.pullRequests) @@ -1597,8 +1598,8 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: { {props.project ? : null} @@ -1617,6 +1618,7 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: { the time/jump label yields to the settle affordance. */} {prBadge} {prBadge && + variantAction !== "unsettle" && pr && (supportsMultiplePullRequests ? visibleThreadPullRequests(thread.pullRequests).length === 0 diff --git a/apps/web/src/components/ThreadStatusIndicators.tsx b/apps/web/src/components/ThreadStatusIndicators.tsx index 2d724a726506..e948aa4091ee 100644 --- a/apps/web/src/components/ThreadStatusIndicators.tsx +++ b/apps/web/src/components/ThreadStatusIndicators.tsx @@ -154,6 +154,7 @@ export function ThreadPullRequestBadgeControl({ onOpenPullRequest: (event: MouseEvent) => void; }) { const isStack = badge?.kind === "stack"; + const linkedCount = badge?.kind === "pull-request" && badge.others > 0 ? badge.others + 1 : null; if (!isStack && (number === undefined || url === undefined)) return null; const label = isStack ? `Stack of ${badge.layers} pull requests, ${badge.state}` @@ -169,16 +170,16 @@ export function ThreadPullRequestBadgeControl({ "text-xs tabular-nums", variant === "ghost" && "font-normal text-xs! active:scale-100 [--control-icon-color:currentColor]", - isStack ? PR_STATE_COLOR_CLASS[badge.state] : (status?.colorClass ?? "text-muted-foreground"), + linkedCount !== null + ? "text-secondary-label" + : isStack + ? PR_STATE_COLOR_CLASS[badge.state] + : (status?.colorClass ?? "text-muted-foreground"), ); const content = ( <> - {isStack - ? badge.layers - : badge?.kind === "pull-request" && badge.others > 0 - ? `+${badge.others}` - : number} + {isStack ? badge.layers : linkedCount !== null ? `+${linkedCount}` : number} ); return (