# Files changed · PR \#22727 · pingdotgg/t3code

[View on GitCafe](https://git.cafe/pingdotgg/t3code/pull/22727/diffs)

Repository: [pingdotgg/t3code](https://git.cafe/pingdotgg/t3code)

Visibility: public

[refactor\(web\): the pull request list's grouping helper lives in a logic module](https://git.cafe/pingdotgg/t3code/pull/22727.md)

Version: 6

Head commit: c5ebd2c16c91a64e4a8f08ee2db17230d6a0ad5e

Base commit: 0290927ccadf16aca93b4ba8349222209c6e6302

Comparison base: 0290927ccadf16aca93b4ba8349222209c6e6302

Showing up to 20 entries from this revision. If the pull request changes, reopen the first page before continuing.

## apps/web/src/components/pullRequest/PullRequestsPage.tsx

Status: modified; +1; −2

```diff
@@ -65,13 +65,12 @@
 import { PullRequestsColumn } from "./PullRequestsColumn";
 import { PullRequestListGroups } from "./PullRequestListGroups";
 import {
-  pullRequestGroups,
-  pullRequestListEntryId,
   usePullRequestDisplayGroups,
   usePullRequestQueryFilters,
   usePullRequestScope,
   type PullRequestsSearch,
 } from "./usePullRequestListView";
+import { pullRequestGroups, pullRequestListEntryId } from "./pullRequestListGroups.logic";
 import { PullRequestListEmptyState } from "./PullRequestListEmptyState";
 import { PullRequestListGhost } from "./PullRequestGhosts";
 import { type PullRequestRowTarget } from "./PullRequestRow";
```

## apps/web/src/components/pullRequest/pullRequestListGroups.logic.ts

Status: added; +127; −0

```diff
@@ -1,0 +1,127 @@
+import type {
+  PullRequestInvolvement,
+  PullRequestListFilters,
+  PullRequestListResult,
+} from "@t3tools/contracts";
+
+import type { PullRequestTabStatusSeed } from "~/components/RightPanelTabs";
+import { pullRequestSurfaceId } from "~/rightPanelStore";
+import {
+  filterPullRequestsByInvolvement,
+  groupPullRequestsByInvolvement,
+  matchesPullRequestFilters,
+  matchesPullRequestQuery,
+  partitionPullRequestsWithPriority,
+  pullRequestEntryKey,
+  pullRequestEntryViewer,
+  type EnvironmentPullRequestEntry,
+  type PullRequestPartitionsSnapshot,
+} from "./pullRequestList.logic";
+
+export const pullRequestListEntryId = (target: Parameters<typeof pullRequestSurfaceId>[0]) =>
+  pullRequestSurfaceId({ ...target, repository: target.repository.toLowerCase() });
+
+/** The held rows narrowed to what the reader asked for, in the groups the list shows. */
+export function pullRequestGroups({
+  known,
+  providers,
+  viewers,
+  involvement,
+  localFilters,
+  hasLocalFilters,
+  text,
+  answeredLocally,
+  baselineEmpty,
+  held,
+  authored,
+  reviewing,
+  partitionsWanted,
+}: {
+  known: ReadonlyArray<EnvironmentPullRequestEntry>;
+  providers: PullRequestListResult["providers"];
+  viewers: PullRequestListResult["viewers"];
+  involvement: PullRequestInvolvement;
+  localFilters: PullRequestListFilters;
+  hasLocalFilters: boolean;
+  text: string;
+  answeredLocally: boolean;
+  baselineEmpty: boolean;
+  held: PullRequestPartitionsSnapshot | undefined;
+  authored: ReadonlyArray<EnvironmentPullRequestEntry> | undefined;
+  reviewing: ReadonlyArray<EnvironmentPullRequestEntry> | undefined;
+  partitionsWanted: boolean;
+}) {
+  const searchingHosts = new Set(
+    providers.flatMap((provider) => (provider.searchesOnHost ? [provider.host] : [])),
+  );
+
+  const entries = (() => {
+    const involvementEntries = filterPullRequestsByInvolvement(known, viewers, involvement);
+    // The hosts search more than the row shows — a body, a review, a commit message — so once
+    // their answer is in, narrowing it again here would throw away matches the reader asked for.
+    // The local pass stands in for the answer that has not arrived yet, and for the hosts that
+    // answered without searching at all: Azure DevOps has no text filter, so its rows arrive
+    // whole and would otherwise sit under a search that never touched them.
+    const narrowedEntries = hasLocalFilters
+      ? involvementEntries.filter((entry) =>
+          matchesPullRequestFilters(entry, localFilters, pullRequestEntryViewer(entry, viewers)),
+        )
+      : involvementEntries;
+    if (text.length === 0) {
+      return narrowedEntries.toSorted((left, right) =>
+        right.updatedAt.localeCompare(left.updatedAt),
+      );
+    }
+    return narrowedEntries.filter(
+      (entry) =>
+        (answeredLocally && searchingHosts.has(entry.host)) || matchesPullRequestQuery(entry, text),
+    );
+  })();
+
+  const listedPullRequestTabStatuses: Record<string, PullRequestTabStatusSeed> = Object.fromEntries(
+    entries.map((entry) => [
+      pullRequestSurfaceId(entry),
+      {
+        state: entry.state,
+        isDraft: entry.isDraft,
+      },
+    ]),
+  );
+
+  const groups = (() => {
+    if (involvement !== "all") return [{ key: "others" as const, label: "", entries }];
+    if (baselineEmpty) {
+      return groupPullRequestsByInvolvement(entries, viewers);
+    }
+    const narrow = (rows: ReadonlyArray<EnvironmentPullRequestEntry> | undefined) =>
+      rows === undefined || !hasLocalFilters
+        ? rows
+        : rows.filter((entry) =>
+            matchesPullRequestFilters(entry, localFilters, pullRequestEntryViewer(entry, viewers)),
+          );
+    const narrowedAuthored = narrow(partitionsWanted ? (authored ?? held?.authored) : undefined);
+    const narrowedReviewing = narrow(partitionsWanted ? (reviewing ?? held?.reviewing) : undefined);
+    if (narrowedAuthored === undefined || narrowedReviewing === undefined) {
+      return groupPullRequestsByInvolvement(entries, viewers);
+    }
+    return partitionPullRequestsWithPriority(entries, narrowedAuthored, narrowedReviewing);
+  })();
+
+  const entriesByStatsKey: ReadonlyMap<string, EnvironmentPullRequestEntry> = new Map(
+    groups.flatMap((group) =>
+      group.entries.map((entry) => [pullRequestEntryKey(entry), entry] as const),
+    ),
+  );
+  const heldPullRequestsBySurface = new Map(
+    groups.flatMap((group) =>
+      group.entries.map((entry) => [pullRequestListEntryId(entry), entry] as const),
+    ),
+  );
+  return {
+    entries,
+    listedPullRequestTabStatuses,
+    groups,
+    entriesByStatsKey,
+    heldPullRequestsBySurface,
+  };
+}
```

## apps/web/src/components/pullRequest/usePullRequestListView.ts

Status: modified; +1; −115

```diff
@@ -11,20 +11,14 @@
 } from "@t3tools/contracts";
 import { useState } from "react";
 
-import type { PullRequestTabStatusSeed } from "~/components/RightPanelTabs";
-import { pullRequestSurfaceId } from "~/rightPanelStore";
 import { useAllEnvironmentShellsBootstrapped, useProjects } from "~/state/entities";
 import { useEnvironments } from "~/state/environments";
 import { useDebouncedValue } from "~/state/queries";
 import {
   applyPullRequestOverrides,
-  filterPullRequestsByInvolvement,
   findProjectForRepository,
   findScopedProject,
-  groupPullRequestsByInvolvement,
   matchesPullRequestFilters,
-  matchesPullRequestQuery,
-  partitionPullRequestsWithPriority,
   pullRequestDiffStatKey,
   pullRequestEntryKey,
   pullRequestEntryViewer,
@@ -40,12 +34,12 @@
   type PullRequestDiffStats,
   type PullRequestGroup,
   type PullRequestListOverride,
-  type PullRequestPartitionsSnapshot,
 } from "./pullRequestList.logic";
 import type { PullRequestExpectedHost } from "./PullRequestListFilters";
 import type { PullRequestListPreferences, PullRequestListSort } from "./pullRequestListPreferences";
 import { assignProjectsToEnvironments } from "./pullRequestProjectAssignment.logic";
 import { pullRequestFilterProjects } from "./pullRequestProjectFilter.logic";
+import { pullRequestListEntryId } from "./pullRequestListGroups.logic";
 
 export interface PullRequestsSearch extends PullRequestListPreferences {
   readonly environmentId?: EnvironmentId;
@@ -60,9 +54,6 @@
 
 const SEARCH_DEBOUNCE_MS = 250;
 
-export const pullRequestListEntryId = (target: Parameters<typeof pullRequestSurfaceId>[0]) =>
-  pullRequestSurfaceId({ ...target, repository: target.repository.toLowerCase() });
-
 /** Which servers and projects the page reads, from the URL and what this client knows. */
 export function usePullRequestScope(search: PullRequestsSearch) {
   const { environments } = useEnvironments();
@@ -225,111 +216,6 @@
   };
 }
 
-/** The held rows narrowed to what the reader asked for, in the groups the list shows. */
-export function pullRequestGroups({
-  known,
-  providers,
-  viewers,
-  involvement,
-  localFilters,
-  hasLocalFilters,
-  text,
-  answeredLocally,
-  baselineEmpty,
-  held,
-  authored,
-  reviewing,
-  partitionsWanted,
-}: {
-  known: ReadonlyArray<EnvironmentPullRequestEntry>;
-  providers: PullRequestListResult["providers"];
-  viewers: PullRequestListResult["viewers"];
-  involvement: PullRequestInvolvement;
-  localFilters: PullRequestListFilters;
-  hasLocalFilters: boolean;
-  text: string;
-  answeredLocally: boolean;
-  baselineEmpty: boolean;
-  held: PullRequestPartitionsSnapshot | undefined;
-  authored: ReadonlyArray<EnvironmentPullRequestEntry> | undefined;
-  reviewing: ReadonlyArray<EnvironmentPullRequestEntry> | undefined;
-  partitionsWanted: boolean;
-}) {
-  const searchingHosts = new Set(
-    providers.flatMap((provider) => (provider.searchesOnHost ? [provider.host] : [])),
-  );
-
-  const entries = (() => {
-    const involvementEntries = filterPullRequestsByInvolvement(known, viewers, involvement);
-    // The hosts search more than the row shows — a body, a review, a commit message — so once
-    // their answer is in, narrowing it again here would throw away matches the reader asked for.
-    // The local pass stands in for the answer that has not arrived yet, and for the hosts that
-    // answered without searching at all: Azure DevOps has no text filter, so its rows arrive
-    // whole and would otherwise sit under a search that never touched them.
-    const narrowedEntries = hasLocalFilters
-      ? involvementEntries.filter((entry) =>
-          matchesPullRequestFilters(entry, localFilters, pullRequestEntryViewer(entry, viewers)),
-        )
-      : involvementEntries;
-    if (text.length === 0) {
-      return narrowedEntries.toSorted((left, right) =>
-        right.updatedAt.localeCompare(left.updatedAt),
-      );
-    }
-    return narrowedEntries.filter(
-      (entry) =>
-        (answeredLocally && searchingHosts.has(entry.host)) || matchesPullRequestQuery(entry, text),
-    );
-  })();
-
-  const listedPullRequestTabStatuses: Record<string, PullRequestTabStatusSeed> = Object.fromEntries(
-    entries.map((entry) => [
-      pullRequestSurfaceId(entry),
-      {
-        state: entry.state,
-        isDraft: entry.isDraft,
-      },
-    ]),
-  );
-
-  const groups = (() => {
-    if (involvement !== "all") return [{ key: "others" as const, label: "", entries }];
-    if (baselineEmpty) {
-      return groupPullRequestsByInvolvement(entries, viewers);
-    }
-    const narrow = (rows: ReadonlyArray<EnvironmentPullRequestEntry> | undefined) =>
-      rows === undefined || !hasLocalFilters
-        ? rows
-        : rows.filter((entry) =>
-            matchesPullRequestFilters(entry, localFilters, pullRequestEntryViewer(entry, viewers)),
-          );
-    const narrowedAuthored = narrow(partitionsWanted ? (authored ?? held?.authored) : undefined);
-    const narrowedReviewing = narrow(partitionsWanted ? (reviewing ?? held?.reviewing) : undefined);
-    if (narrowedAuthored === undefined || narrowedReviewing === undefined) {
-      return groupPullRequestsByInvolvement(entries, viewers);
-    }
-    return partitionPullRequestsWithPriority(entries, narrowedAuthored, narrowedReviewing);
-  })();
-
-  const entriesByStatsKey: ReadonlyMap<string, EnvironmentPullRequestEntry> = new Map(
-    groups.flatMap((group) =>
-      group.entries.map((entry) => [pullRequestEntryKey(entry), entry] as const),
-    ),
-  );
-  const heldPullRequestsBySurface = new Map(
-    groups.flatMap((group) =>
-      group.entries.map((entry) => [pullRequestListEntryId(entry), entry] as const),
-    ),
-  );
-  return {
-    entries,
-    listedPullRequestTabStatuses,
-    groups,
-    entriesByStatsKey,
-    heldPullRequestsBySurface,
-  };
-}
-
 /**
  * The hosts the list has answered for (kept while a host filter narrows the answer), and the
  * groups as shown with measured sizes, optimistic actions, and the reader's sort.
```
