authorgravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-09-10 11:34:55-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-09-10 11:34:55-07:00
log0527ddf06defc88169c245a0fe53635d41d66469
tree78065c97c8f2c63fd9aa08b5bb2bda4cd2e45b57
parent0882431e0fc0fabe950743dd44b674a07a584bc9
signature Signed by PGP key B5690EEEBB952194

fix: quiet settled threads and simplify PR badges (#11101)


6 files changed, 26 insertions(+), 23 deletions(-)

apps/mobile/src/features/threads/thread-list-items.tsx+2-2
......@@ -44,11 +44,11 @@ export const THREAD_LIST_COMPACT_INSET = HOME_HORIZONTAL_INSET;
4444const SIDEBAR_ROW_RADIUS = 12;
4545
4646function pullRequestTintColor(
47 pr: Pick<ThreadPrPresentation, "state" | "isDraft">,
47 pr: Pick<ThreadPrPresentation, "state" | "isDraft" | "others">,
4848 colorScheme: "light" | "dark",
4949) {
5050 const dark = colorScheme === "dark";
51 if (pr.state === "open" && pr.isDraft === true) {
51 if (pr.others > 0 || (pr.state === "open" && pr.isDraft === true)) {
5252 return dark ? "#a1a1aa" : "#71717a";
5353 }
5454 switch (pr.state) {
apps/mobile/src/features/threads/thread-list-v2-items.tsx+1-7
......@@ -880,13 +880,7 @@ export const ThreadListV2Row = memo(function ThreadListV2Row(props: {
880880 ? materialYouStyleLayoutActive
881881 ? "accent-thread-selected-foreground"
882882 : "accent-user-bubble-foreground"
883 : pr.kind === "stack" || pr.isDraft || pr.state === null
884 ? "accent-foreground-muted"
885 : pr.state === "open"
886 ? "accent-adaptive-emerald-600-400"
887 : pr.state === "merged"
888 ? "accent-adaptive-violet-600-400"
889 : "accent-foreground-muted"
883 : "accent-foreground-muted"
890884 }
891885 />
892886 ) : null}
apps/mobile/src/state/thread-pr-presentation.ts+7-3
......@@ -65,11 +65,12 @@ export function presentThreadLinkedPullRequests(
6565 const snapshot = link.snapshot;
6666 const state = badge.kind === "stack" ? badge.state : (snapshot?.state ?? null);
6767 const isDraft = snapshot?.isDraft === true && state === "open";
68 const linkedCount = badge.kind === "pull-request" && badge.others > 0 ? badge.others + 1 : null;
6869 const label =
6970 badge.kind === "stack"
7071 ? String(badge.layers)
71 : badge.others > 0
72 ? `+${badge.others}`
72 : linkedCount !== null
73 ? `+${linkedCount}`
7374 : String(link.number);
7475 return {
7576 kind: badge.kind,
......@@ -84,7 +85,10 @@ export function presentThreadLinkedPullRequests(
8485 badge.kind === "stack"
8586 ? `${badge.layers} pull requests in stack, ${state ?? "status pending"}`
8687 : `#${link.number} pull request ${state === null ? "status pending" : isDraft ? "draft" : state}${badge.others > 0 ? `, ${badge.others} more linked` : ""}`,
87 textClassName: state === null || isDraft ? "text-foreground-muted" : PR_STATE_TEXT_CLASS[state],
88 textClassName:
89 linkedCount !== null || state === null || isDraft
90 ? "text-foreground-muted"
91 : PR_STATE_TEXT_CLASS[state],
8892 };
8993}
9094
apps/mobile/src/state/use-thread-pr.test.ts+3-1
......@@ -87,7 +87,9 @@ describe("presentThreadLinkedPullRequests", () => {
8787 it("counts unrelated links without labelling them a stack", () => {
8888 expect(presentThreadLinkedPullRequests([linkedPr(1), linkedPr(2)])).toMatchObject({
8989 kind: "pull-request",
90 label: "+1",
90 label: "+2",
91 others: 1,
92 textClassName: "text-foreground-muted",
9193 });
9294 });
9395
apps/web/src/components/Sidebar.tsx+6-4
......@@ -1387,6 +1387,7 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: {
13871387 // content; surface is reserved for interaction (hover, multi-select, route).
13881388 const rowSurfaceClassName = cn(
13891389 "group/sidebar-row relative w-full cursor-pointer overflow-hidden rounded-md text-left outline-none select-none",
1390 variantAction === "unsettle" && "[&:not(:hover):not(:focus-within)_*]:text-secondary-label/70",
13901391 props.isActive
13911392 ? "bg-sidebar-row-active text-sidebar-foreground"
13921393 : isSelected
......@@ -1469,7 +1470,7 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: {
14691470 : "text-foreground/90",
14701471 )
14711472 : cn(
1472 "truncate group-hover/sidebar-row:text-foreground",
1473 "truncate group-focus-within/sidebar-row:text-foreground group-hover/sidebar-row:text-foreground",
14731474 shouldRecede
14741475 ? "text-secondary-label/70"
14751476 : props.isActive || isWoke
......@@ -1485,7 +1486,7 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: {
14851486 </span>
14861487 );
14871488
1488 // Stacks show their layer count; unrelated links show only the remainder count.
1489 // Stacks show their layer count; multiple unrelated links show their total count.
14891490 // Plain clicks open T3; individual PR links also support opening the host in a new tab.
14901491 const prBadgeShape = supportsMultiplePullRequests
14911492 ? resolveThreadPullRequestBadge(thread.pullRequests)
......@@ -1597,8 +1598,8 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: {
15971598 <span
15981599 className={cn(
15991600 "shrink-0 transition-opacity",
1600 !props.isActive &&
1601 "opacity-40 grayscale group-hover/sidebar-row:opacity-100 group-hover/sidebar-row:grayscale-0",
1601 (!props.isActive || variantAction === "unsettle") &&
1602 "opacity-40 grayscale group-focus-within/sidebar-row:opacity-100 group-focus-within/sidebar-row:grayscale-0 group-hover/sidebar-row:opacity-100 group-hover/sidebar-row:grayscale-0",
16021603 )}
16031604 >
16041605 {props.project ? <ProjectFavicon project={props.project} className="size-4" /> : null}
......@@ -1617,6 +1618,7 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: {
16171618 the time/jump label yields to the settle affordance. */}
16181619 {prBadge}
16191620 {prBadge &&
1621 variantAction !== "unsettle" &&
16201622 pr &&
16211623 (supportsMultiplePullRequests
16221624 ? visibleThreadPullRequests(thread.pullRequests).length === 0
apps/web/src/components/ThreadStatusIndicators.tsx+7-6
......@@ -154,6 +154,7 @@ export function ThreadPullRequestBadgeControl({
154154 onOpenPullRequest: (event: MouseEvent<HTMLAnchorElement>) => void;
155155}) {
156156 const isStack = badge?.kind === "stack";
157 const linkedCount = badge?.kind === "pull-request" && badge.others > 0 ? badge.others + 1 : null;
157158 if (!isStack && (number === undefined || url === undefined)) return null;
158159 const label = isStack
159160 ? `Stack of ${badge.layers} pull requests, ${badge.state}`
......@@ -169,16 +170,16 @@ export function ThreadPullRequestBadgeControl({
169170 "text-xs tabular-nums",
170171 variant === "ghost" &&
171172 "font-normal text-xs! active:scale-100 [--control-icon-color:currentColor]",
172 isStack ? PR_STATE_COLOR_CLASS[badge.state] : (status?.colorClass ?? "text-muted-foreground"),
173 linkedCount !== null
174 ? "text-secondary-label"
175 : isStack
176 ? PR_STATE_COLOR_CLASS[badge.state]
177 : (status?.colorClass ?? "text-muted-foreground"),
173178 );
174179 const content = (
175180 <>
176181 <ThreadPullRequestBadgeIcon icon={badge?.kind ?? "pull-request"} />
177 {isStack
178 ? badge.layers
179 : badge?.kind === "pull-request" && badge.others > 0
180 ? `+${badge.others}`
181 : number}
182 {isStack ? badge.layers : linkedCount !== null ? `+${linkedCount}` : number}
182183 </>
183184 );
184185 return (