From 4de372cdc73c8dd6845205beccdf68a3f764daca Mon Sep 17 00:00:00 2001 From: Theo Browne Date: Tue, 20 Oct 2026 00:24:06 -0700 Subject: [PATCH 1/3] feat(web): split PR diff stat into code, tests, mocks, and generated Co-Authored-By: Claude Opus 5.5 --- .../pull-requests/PullRequestFiles.tsx | 32 ++++++ .../pull-requests/pullRequestFileFilters.ts | 17 +-- apps/server/src/auth/RpcAuthorization.ts | 1 + .../src/observability/RpcInstrumentation.ts | 1 + .../src/pullRequest/PullRequestService.ts | 37 +++++++ .../pullRequestChangeBreakdown.test.ts | 33 ++++++ .../pullRequest/pullRequestChangeBreakdown.ts | 15 +++ apps/server/src/ws.ts | 2 + .../PullRequestChangeBreakdownStat.tsx | 100 ++++++++++++++++++ .../pullRequest/PullRequestDetailPanel.tsx | 10 +- .../src/state/pullRequestRouting.ts | 1 + .../client-runtime/src/state/pullRequests.ts | 8 ++ packages/contracts/src/pullRequest.ts | 20 ++++ packages/contracts/src/rpc.ts | 9 ++ packages/shared/package.json | 4 + packages/shared/src/changedFileKind.test.ts | 40 +++++++ packages/shared/src/changedFileKind.ts | 76 +++++++++++++ .../src/server/PullRequestProvider.ts | 15 +++ .../src/server/GitHubPullRequestApi.ts | 42 ++++++++ .../src/server/GitHubPullRequestProvider.ts | 2 + .../src/server/gitHubPullRequestJson.ts | 24 +++++ .../src/server/pullRequestReads.graphql | 18 ++++ .../src/server/pullRequestReads.graphql.ts | 78 ++++++++++++-- 23 files changed, 562 insertions(+), 23 deletions(-) create mode 100644 apps/server/src/pullRequest/pullRequestChangeBreakdown.test.ts create mode 100644 apps/server/src/pullRequest/pullRequestChangeBreakdown.ts create mode 100644 apps/web/src/components/pullRequest/PullRequestChangeBreakdownStat.tsx create mode 100644 packages/shared/src/changedFileKind.test.ts create mode 100644 packages/shared/src/changedFileKind.ts diff --git a/apps/mobile/src/features/pull-requests/PullRequestFiles.tsx b/apps/mobile/src/features/pull-requests/PullRequestFiles.tsx index f699e46d6fb0..dc9c765bf506 100644 --- a/apps/mobile/src/features/pull-requests/PullRequestFiles.tsx +++ b/apps/mobile/src/features/pull-requests/PullRequestFiles.tsx @@ -1,3 +1,4 @@ +import type { PullRequestChangeBreakdown } from "@t3tools/contracts"; import { PullRequestSectionHeading } from "./PullRequestSectionHeading"; import { AnimatedLegendList } from "@legendapp/list/reanimated"; import Animated from "react-native-reanimated"; @@ -23,6 +24,33 @@ import { import { usePullRequestFilesViewed } from "./usePullRequestFilesViewed"; import { usePullRequestFileFilter } from "./usePullRequestFileFilter"; +const BREAKDOWN_LABELS = { + code: "Code", + tests: "Tests", + mocks: "Mocks", + generated: "Generated", +} satisfies Record, string>; + +function PullRequestChangeBreakdownLine(props: { + readonly breakdown: NonNullable; +}) { + const parts = (Object.keys(BREAKDOWN_LABELS) as Array).flatMap( + (kind) => { + const { additions, deletions } = props.breakdown[kind]; + return additions === 0 && deletions === 0 + ? [] + : [ + `${BREAKDOWN_LABELS[kind]} +${additions.toLocaleString()} −${deletions.toLocaleString()}`, + ]; + }, + ); + return ( + + {parts.join(" · ")} + + ); +} + function fileDirectory(path: string) { const separator = path.lastIndexOf("/"); return separator < 0 ? "" : path.slice(0, separator); @@ -37,6 +65,9 @@ export function usePullRequestFilesContent(props: { const result = usePullRequestDiff(props.params); const detail = useEnvironmentQuery(composerPullRequests.detail(requestTarget(props.params))); const viewed = usePullRequestFilesViewed(props.params, props.params.commit ? null : detail.data); + const breakdown = useEnvironmentQuery( + props.params.commit ? null : composerPullRequests.changeBreakdown(requestTarget(props.params)), + ).data; const filter = usePullRequestFileFilter(result.parsed); const parsed = filter.parsed; const loading = result.isPending && parsed.kind === "empty" && !result.error; @@ -209,6 +240,7 @@ export function usePullRequestFilesContent(props: { ? `${filter.hiddenCount} test ${filter.hiddenCount === 1 ? "file" : "files"} hidden` : "Test files included"} + {breakdown ? : null}