Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions apps/mobile/src/features/pull-requests/PullRequestsScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import {
scopedPullRequestListKey as pullRequestKey,
type EnvironmentPullRequestListEntry,
withoutScratchProjectScope,
pullRequestProjectChoices,
} from "./pullRequestList";
import { useDebouncedValue } from "../../state/queries";
import { useWorkspaceTabs } from "../workspace/WorkspaceTabsContext";
Expand Down Expand Up @@ -222,10 +223,14 @@ export function PullRequestsScreen(
}
const labelColors =
knownFacets.environmentId === environmentId ? knownFacets.labelColors : facets.labelColors;
const projects = allProjects.filter(
(project) =>
!project.isScratch && (environmentId === null || project.environmentId === environmentId),
);
const projects = pullRequestProjectChoices(allProjects, {
environmentId,
controls,
environmentLabels: new Map(
environments.map((item) => [item.environmentId, item.environmentLabel]),
),
servesPullRequests: (id) => configs.get(id)?.environment.capabilities.pullRequests === true,
});
const filters = usePullRequestListFilters({
environments: environments.map((item) => ({
id: String(item.environmentId),
Expand Down
49 changes: 49 additions & 0 deletions apps/mobile/src/features/pull-requests/pullRequestList.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { describe, expect, it } from "vite-plus/test";
import {
EnvironmentId,
ProjectId,
SourceControlProviderKind,
type PullRequestListEntry,
type PullRequestListResult,
Expand All @@ -23,6 +24,7 @@ import {
pullRequestListRowsAreEqual,
withShownPullRequestStats,
withoutScratchProjectScope,
pullRequestProjectChoices,
} from "./pullRequestList";

function entry(number: number, patch: Partial<PullRequestListEntry> = {}): PullRequestListEntry {
Expand Down Expand Up @@ -399,6 +401,53 @@ describe("pullRequestFilterChoices", () => {
});
});

describe("pullRequestProjectChoices", () => {
const laptop = EnvironmentId.make("env-laptop");
const desktop = EnvironmentId.make("env-desktop");
const project = (id: string, environmentId: EnvironmentId) => ({
id: ProjectId.make(id),
environmentId,
title: "t3code",
workspaceRoot: `/work/${id}`,
repositoryIdentity: { canonicalKey: "github.com/pingdotgg/t3code" },
});
const projects = [project("main", laptop), project("worktree", laptop), project("main", desktop)];
const choices = (
servesPullRequests: (environmentId: EnvironmentId) => boolean,
scope: { projectId?: string; projectEnvironmentId?: string } = {},
) =>
pullRequestProjectChoices(projects, {
environmentId: null,
controls: { ...DEFAULT_PR_LIST_CONTROLS, ...scope },
environmentLabels: new Map([
[laptop, "laptop"],
[desktop, "desktop"],
]),
servesPullRequests,
}).map(({ id, environmentId, title }) => ({ id, environmentId, title }));

it("lists each repository once per environment that serves pull requests", () => {
expect(choices(() => true)).toEqual([
{ id: "main", environmentId: desktop, title: "t3code · desktop" },
{ id: "main", environmentId: laptop, title: "t3code · laptop" },
]);
expect(choices((environmentId) => environmentId === desktop)).toEqual([
{ id: "main", environmentId: desktop, title: "t3code" },
]);
});

it("keeps the saved scope when its environment stops serving or has not loaded", () => {
const scope = { projectId: "worktree", projectEnvironmentId: laptop };
expect(choices((environmentId) => environmentId === desktop, scope)).toEqual([
{ id: "main", environmentId: desktop, title: "t3code · desktop" },
{ id: "worktree", environmentId: laptop, title: "t3code · laptop" },
]);
expect(choices(() => false, scope)).toEqual([
{ id: "worktree", environmentId: laptop, title: "t3code" },
]);
});
});

describe("shown PR diff stats", () => {
const cups = EnvironmentId.make("cups");
const stat = (number: number, additions: number, deletions: number) => ({
Expand Down
29 changes: 29 additions & 0 deletions apps/mobile/src/features/pull-requests/pullRequestList.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { pullRequestFilterProjects } from "@t3tools/client-runtime/pull-request-project-filter";
import { parsePullRequestQuery } from "@t3tools/client-runtime/pull-request-query";
import {
ProjectId,
Expand Down Expand Up @@ -33,6 +34,34 @@ export function withoutScratchProjectScope(
: controls;
}

type FilterProject = Parameters<typeof pullRequestFilterProjects>[0][number];

export function pullRequestProjectChoices<
Project extends FilterProject & { readonly isScratch?: true },
>(
projects: readonly Project[],
input: {
readonly environmentId: string | null;
readonly controls: PullRequestListControls;
readonly environmentLabels: ReadonlyMap<EnvironmentId, string>;
readonly servesPullRequests: (environmentId: EnvironmentId) => boolean;
},
) {
const isSelected = (project: Project) =>
project.id === input.controls.projectId &&
project.environmentId === input.controls.projectEnvironmentId;
return pullRequestFilterProjects(
projects.filter(
(project) =>
!project.isScratch &&
(input.environmentId === null || project.environmentId === input.environmentId) &&
(input.servesPullRequests(project.environmentId) || isSelected(project)),
),
input.environmentLabels,
projects.find(isSelected),
);
}

export function pullRequestListKey(
item: Pick<PullRequestListEntry, "host" | "repository" | "number">,
) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { EnvironmentId, ProjectId } from "@t3tools/contracts";
import { describe, expect, it } from "vite-plus/test";

import { findScopedProject } from "./pullRequestList.logic";
import { pullRequestFilterProjects } from "./pullRequestProjectFilter.logic";
import { pullRequestFilterProjects } from "@t3tools/client-runtime/pull-request-project-filter";

const cups = EnvironmentId.make("env-cups");
const nucbox = EnvironmentId.make("env-nucbox");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ import {
import type { PullRequestExpectedHost } from "./PullRequestListFilters";
import type { PullRequestListPreferences, PullRequestListSort } from "./pullRequestListPreferences";
import { assignProjectsToEnvironments } from "./pullRequestProjectAssignment.logic";
import { pullRequestFilterProjects } from "./pullRequestProjectFilter.logic";
import { pullRequestFilterProjects } from "@t3tools/client-runtime/pull-request-project-filter";
import { pullRequestListEntryId } from "./pullRequestListGroups.logic";

export interface PullRequestsSearch extends PullRequestListPreferences {
Expand Down
4 changes: 4 additions & 0 deletions packages/client-runtime/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,10 @@
"types": "./src/threadReference.ts",
"default": "./src/threadReference.ts"
},
"./pull-request-project-filter": {
"types": "./src/pullRequestProjectFilter.ts",
"default": "./src/pullRequestProjectFilter.ts"
},
"./thread-pull-request-compatibility": {
"types": "./src/threadPullRequestCompatibility.ts",
"default": "./src/threadPullRequestCompatibility.ts"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import type { EnvironmentId } from "@t3tools/contracts";
import type { EnvironmentId, ProjectId } from "@t3tools/contracts";

import type { AssignableProject } from "./pullRequestProjectAssignment.logic";

interface FilterProject extends AssignableProject {
interface FilterProject {
readonly id: ProjectId;
readonly environmentId: EnvironmentId;
readonly repositoryIdentity?: { readonly canonicalKey?: string | undefined } | null | undefined;
readonly title: string;
readonly workspaceRoot: string;
}
Expand All @@ -25,7 +26,7 @@ function distinguishTitles<Project extends FilterProject>(
export function pullRequestFilterProjects<Project extends FilterProject>(
projects: ReadonlyArray<Project>,
environmentLabels: ReadonlyMap<EnvironmentId, string>,
selectedProject?: Pick<AssignableProject, "id" | "environmentId">,
selectedProject?: Pick<FilterProject, "id" | "environmentId">,
) {
const byRepository = new Map<string, Project>();
for (const project of projects) {
Expand All @@ -45,7 +46,7 @@ export function pullRequestFilterProjects<Project extends FilterProject>(
);
const byPath = distinguishTitles(byServer, (project) => project.workspaceRoot);
const byEnvironment = distinguishTitles(byPath, (project) => project.environmentId);
return distinguishTitles(byEnvironment, (project) => project.id).toSorted((left, right) =>
return distinguishTitles(byEnvironment, (project) => project.id).sort((left, right) =>
left.title.localeCompare(right.title),
);
}
Loading