| author | |
| committer | |
| log | 0527ddf06defc88169c245a0fe53635d41d66469 |
| tree | 78065c97c8f2c63fd9aa08b5bb2bda4cd2e45b57 |
| parent | 0882431e0fc0fabe950743dd44b674a07a584bc9 |
| signature | Signed by PGP key B5690EEEBB952194 |
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; | ... | @@ -44,11 +44,11 @@ export const THREAD_LIST_COMPACT_INSET = HOME_HORIZONTAL_INSET; |
| 44 | const SIDEBAR_ROW_RADIUS = 12; | 44 | const SIDEBAR_ROW_RADIUS = 12; |
| 45 | 45 | ||
| 46 | function pullRequestTintColor( | 46 | function pullRequestTintColor( |
| 47 | pr: Pick<ThreadPrPresentation, "state" | "isDraft">, | 47 | pr: Pick<ThreadPrPresentation, "state" | "isDraft" | "others">, |
| 48 | colorScheme: "light" | "dark", | 48 | colorScheme: "light" | "dark", |
| 49 | ) { | 49 | ) { |
| 50 | const dark = colorScheme === "dark"; | 50 | const dark = colorScheme === "dark"; |
| 51 | if (pr.state === "open" && pr.isDraft === true) { | 51 | if (pr.others > 0 || (pr.state === "open" && pr.isDraft === true)) { |
| 52 | return dark ? "#a1a1aa" : "#71717a"; | 52 | return dark ? "#a1a1aa" : "#71717a"; |
| 53 | } | 53 | } |
| 54 | switch (pr.state) { | 54 | 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: { | ... | @@ -880,13 +880,7 @@ export const ThreadListV2Row = memo(function ThreadListV2Row(props: { |
| 880 | ? materialYouStyleLayoutActive | 880 | ? materialYouStyleLayoutActive |
| 881 | ? "accent-thread-selected-foreground" | 881 | ? "accent-thread-selected-foreground" |
| 882 | : "accent-user-bubble-foreground" | 882 | : "accent-user-bubble-foreground" |
| 883 | : pr.kind === "stack" || pr.isDraft || pr.state === null | 883 | : "accent-foreground-muted" |
| 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" | ||
| 890 | } | 884 | } |
| 891 | /> | 885 | /> |
| 892 | ) : null} | 886 | ) : null} |
apps/mobile/src/state/thread-pr-presentation.ts+7-3| ... | @@ -65,11 +65,12 @@ export function presentThreadLinkedPullRequests( | ... | @@ -65,11 +65,12 @@ export function presentThreadLinkedPullRequests( |
| 65 | const snapshot = link.snapshot; | 65 | const snapshot = link.snapshot; |
| 66 | const state = badge.kind === "stack" ? badge.state : (snapshot?.state ?? null); | 66 | const state = badge.kind === "stack" ? badge.state : (snapshot?.state ?? null); |
| 67 | const isDraft = snapshot?.isDraft === true && state === "open"; | 67 | const isDraft = snapshot?.isDraft === true && state === "open"; |
| 68 | const linkedCount = badge.kind === "pull-request" && badge.others > 0 ? badge.others + 1 : null; | ||
| 68 | const label = | 69 | const label = |
| 69 | badge.kind === "stack" | 70 | badge.kind === "stack" |
| 70 | ? String(badge.layers) | 71 | ? String(badge.layers) |
| 71 | : badge.others > 0 | 72 | : linkedCount !== null |
| 72 | ? `+${badge.others}` | 73 | ? `+${linkedCount}` |
| 73 | : String(link.number); | 74 | : String(link.number); |
| 74 | return { | 75 | return { |
| 75 | kind: badge.kind, | 76 | kind: badge.kind, |
| ... | @@ -84,7 +85,10 @@ export function presentThreadLinkedPullRequests( | ... | @@ -84,7 +85,10 @@ export function presentThreadLinkedPullRequests( |
| 84 | badge.kind === "stack" | 85 | badge.kind === "stack" |
| 85 | ? `${badge.layers} pull requests in stack, ${state ?? "status pending"}` | 86 | ? `${badge.layers} pull requests in stack, ${state ?? "status pending"}` |
| 86 | : `#${link.number} pull request ${state === null ? "status pending" : isDraft ? "draft" : state}${badge.others > 0 ? `, ${badge.others} more linked` : ""}`, | 87 | : `#${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], | ||
| 88 | }; | 92 | }; |
| 89 | } | 93 | } |
| 90 | 94 |
apps/mobile/src/state/use-thread-pr.test.ts+3-1| ... | @@ -87,7 +87,9 @@ describe("presentThreadLinkedPullRequests", () => { | ... | @@ -87,7 +87,9 @@ describe("presentThreadLinkedPullRequests", () => { |
| 87 | it("counts unrelated links without labelling them a stack", () => { | 87 | it("counts unrelated links without labelling them a stack", () => { |
| 88 | expect(presentThreadLinkedPullRequests([linkedPr(1), linkedPr(2)])).toMatchObject({ | 88 | expect(presentThreadLinkedPullRequests([linkedPr(1), linkedPr(2)])).toMatchObject({ |
| 89 | kind: "pull-request", | 89 | kind: "pull-request", |
| 90 | label: "+1", | 90 | label: "+2", |
| 91 | others: 1, | ||
| 92 | textClassName: "text-foreground-muted", | ||
| 91 | }); | 93 | }); |
| 92 | }); | 94 | }); |
| 93 | 95 |
apps/web/src/components/Sidebar.tsx+6-4| ... | @@ -1387,6 +1387,7 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: { | ... | @@ -1387,6 +1387,7 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: { |
| 1387 | // content; surface is reserved for interaction (hover, multi-select, route). | 1387 | // content; surface is reserved for interaction (hover, multi-select, route). |
| 1388 | const rowSurfaceClassName = cn( | 1388 | const rowSurfaceClassName = cn( |
| 1389 | "group/sidebar-row relative w-full cursor-pointer overflow-hidden rounded-md text-left outline-none select-none", | 1389 | "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", | ||
| 1390 | props.isActive | 1391 | props.isActive |
| 1391 | ? "bg-sidebar-row-active text-sidebar-foreground" | 1392 | ? "bg-sidebar-row-active text-sidebar-foreground" |
| 1392 | : isSelected | 1393 | : isSelected |
| ... | @@ -1469,7 +1470,7 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: { | ... | @@ -1469,7 +1470,7 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: { |
| 1469 | : "text-foreground/90", | 1470 | : "text-foreground/90", |
| 1470 | ) | 1471 | ) |
| 1471 | : cn( | 1472 | : cn( |
| 1472 | "truncate group-hover/sidebar-row:text-foreground", | 1473 | "truncate group-focus-within/sidebar-row:text-foreground group-hover/sidebar-row:text-foreground", |
| 1473 | shouldRecede | 1474 | shouldRecede |
| 1474 | ? "text-secondary-label/70" | 1475 | ? "text-secondary-label/70" |
| 1475 | : props.isActive || isWoke | 1476 | : props.isActive || isWoke |
| ... | @@ -1485,7 +1486,7 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: { | ... | @@ -1485,7 +1486,7 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: { |
| 1485 | </span> | 1486 | </span> |
| 1486 | ); | 1487 | ); |
| 1487 | 1488 | ||
| 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. |
| 1489 | // Plain clicks open T3; individual PR links also support opening the host in a new tab. | 1490 | // Plain clicks open T3; individual PR links also support opening the host in a new tab. |
| 1490 | const prBadgeShape = supportsMultiplePullRequests | 1491 | const prBadgeShape = supportsMultiplePullRequests |
| 1491 | ? resolveThreadPullRequestBadge(thread.pullRequests) | 1492 | ? resolveThreadPullRequestBadge(thread.pullRequests) |
| ... | @@ -1597,8 +1598,8 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: { | ... | @@ -1597,8 +1598,8 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: { |
| 1597 | <span | 1598 | <span |
| 1598 | className={cn( | 1599 | className={cn( |
| 1599 | "shrink-0 transition-opacity", | 1600 | "shrink-0 transition-opacity", |
| 1600 | !props.isActive && | 1601 | (!props.isActive || variantAction === "unsettle") && |
| 1601 | "opacity-40 grayscale group-hover/sidebar-row:opacity-100 group-hover/sidebar-row:grayscale-0", | 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", |
| 1602 | )} | 1603 | )} |
| 1603 | > | 1604 | > |
| 1604 | {props.project ? <ProjectFavicon project={props.project} className="size-4" /> : null} | 1605 | {props.project ? <ProjectFavicon project={props.project} className="size-4" /> : null} |
| ... | @@ -1617,6 +1618,7 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: { | ... | @@ -1617,6 +1618,7 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: { |
| 1617 | the time/jump label yields to the settle affordance. */} | 1618 | the time/jump label yields to the settle affordance. */} |
| 1618 | {prBadge} | 1619 | {prBadge} |
| 1619 | {prBadge && | 1620 | {prBadge && |
| 1621 | variantAction !== "unsettle" && | ||
| 1620 | pr && | 1622 | pr && |
| 1621 | (supportsMultiplePullRequests | 1623 | (supportsMultiplePullRequests |
| 1622 | ? visibleThreadPullRequests(thread.pullRequests).length === 0 | 1624 | ? visibleThreadPullRequests(thread.pullRequests).length === 0 |
apps/web/src/components/ThreadStatusIndicators.tsx+7-6| ... | @@ -154,6 +154,7 @@ export function ThreadPullRequestBadgeControl({ | ... | @@ -154,6 +154,7 @@ export function ThreadPullRequestBadgeControl({ |
| 154 | onOpenPullRequest: (event: MouseEvent<HTMLAnchorElement>) => void; | 154 | onOpenPullRequest: (event: MouseEvent<HTMLAnchorElement>) => void; |
| 155 | }) { | 155 | }) { |
| 156 | const isStack = badge?.kind === "stack"; | 156 | const isStack = badge?.kind === "stack"; |
| 157 | const linkedCount = badge?.kind === "pull-request" && badge.others > 0 ? badge.others + 1 : null; | ||
| 157 | if (!isStack && (number === undefined || url === undefined)) return null; | 158 | if (!isStack && (number === undefined || url === undefined)) return null; |
| 158 | const label = isStack | 159 | const label = isStack |
| 159 | ? `Stack of ${badge.layers} pull requests, ${badge.state}` | 160 | ? `Stack of ${badge.layers} pull requests, ${badge.state}` |
| ... | @@ -169,16 +170,16 @@ export function ThreadPullRequestBadgeControl({ | ... | @@ -169,16 +170,16 @@ export function ThreadPullRequestBadgeControl({ |
| 169 | "text-xs tabular-nums", | 170 | "text-xs tabular-nums", |
| 170 | variant === "ghost" && | 171 | variant === "ghost" && |
| 171 | "font-normal text-xs! active:scale-100 [--control-icon-color:currentColor]", | 172 | "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"), | ||
| 173 | ); | 178 | ); |
| 174 | const content = ( | 179 | const content = ( |
| 175 | <> | 180 | <> |
| 176 | <ThreadPullRequestBadgeIcon icon={badge?.kind ?? "pull-request"} /> | 181 | <ThreadPullRequestBadgeIcon icon={badge?.kind ?? "pull-request"} /> |
| 177 | {isStack | 182 | {isStack ? badge.layers : linkedCount !== null ? `+${linkedCount}` : number} |
| 178 | ? badge.layers | ||
| 179 | : badge?.kind === "pull-request" && badge.others > 0 | ||
| 180 | ? `+${badge.others}` | ||
| 181 | : number} | ||
| 182 | </> | 183 | </> |
| 183 | ); | 184 | ); |
| 184 | return ( | 185 | return ( |