Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
21 changes: 16 additions & 5 deletions apps/web/src/components/ChatView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,10 @@ import {
selectThreadPreviewMiniPlayer,
usePreviewMiniPlayerStore,
} from "../previewMiniPlayerStore";
import { pullRequestPanelContext } from "./pullRequest/pullRequestDetail.logic";
import {
pullRequestPanelContext,
threadPullRequestPanelTarget,
} from "./pullRequest/pullRequestDetail.logic";
import { PullRequestDetailPanel } from "./pullRequest/PullRequestDetailPanel";
import { PullRequestDetailGhost } from "./pullRequest/PullRequestGhosts";
import { PullRequestsUnavailableState } from "./pullRequest/PullRequestsUnavailableState";
Expand Down Expand Up @@ -6613,12 +6616,20 @@ export default function ChatView(props: ChatViewProps) {
},
);
}, [activeThreadReferenceCopyTarget]);
const pullRequestPanelTarget = activeThread
? threadPullRequestPanelTarget({
projectId: activeThread.projectId,
pullRequests: visiblePullRequests,
linkedPullRequest: linkedThreadPullRequest,
branchPullRequest: activeThreadShell?.branchPullRequest ?? activeThread.branchPullRequest,
Comment thread
macroscopeapp[bot] marked this conversation as resolved.
})
: null;
const addPullRequestSurface = useCallback(() => {
if (!supportsPullRequests || activeThreadRef === null || linkedThreadPullRequest === null)
if (!supportsPullRequests || activeThreadRef === null || pullRequestPanelTarget === null)
return;
useRightPanelStore.getState().openPullRequest(activeThreadRef, linkedThreadPullRequest);
}, [activeThreadRef, linkedThreadPullRequest, supportsPullRequests]);
const pullRequestSurfaceAvailable = supportsPullRequests && linkedThreadPullRequest !== null;
useRightPanelStore.getState().openPullRequest(activeThreadRef, pullRequestPanelTarget);
}, [activeThreadRef, pullRequestPanelTarget, supportsPullRequests]);
const pullRequestSurfaceAvailable = supportsPullRequests && pullRequestPanelTarget !== null;
const supportsSettlement = serverConfig?.environment.capabilities.threadSettlement === true;
const supportsSnooze = serverConfig?.environment.capabilities.threadSnooze === true;
const supportsPinning = serverConfig?.environment.capabilities.threadPinning === true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import {
isStackedPullRequestBase,
loadingPullRequestCheckoutCommand,
pullRequestPanelContext,
threadPullRequestPanelTarget,
latestPullRequestReviewOutcomes,
newestPullRequestCommitAt,
mergePullRequestThreadComments,
Expand Down Expand Up @@ -1535,6 +1536,54 @@ describe("pull request panel context beside a thread", () => {
);
expect(pullRequestPanelContext({ projectId: null }, surface(3))).toBe("page");
});

describe("the Pull request entry's target", () => {
const legacy = (number: number) => ({
projectId: ProjectId.make("proj-a"),
repository: "pingdotgg/t3code",
number,
url: `https://github.com/pingdotgg/t3code/pull/${number}`,
});

it("opens a linked pull request the legacy field never named", () => {
// An agent's link_pull_request writes only the link list; the legacy field stays null.
const thread = {
projectId: "proj-a",
pullRequests: [link(15046, { source: "agent" })],
linkedPullRequest: null,
};
expect(threadPullRequestPanelTarget(thread)).toEqual({
projectId: "proj-a",
host: "github.com",
repository: "pingdotgg/t3code",
number: 15046,
url: "https://github.com/pingdotgg/t3code/pull/15046",
});
});

it("prefers the link list over a branch pull request", () => {
const thread = {
projectId: "proj-a",
pullRequests: [link(20, { source: "manual" })],
branchPullRequest: legacy(30),
};
expect(threadPullRequestPanelTarget(thread)?.number).toBe(20);
});

it("falls back to the legacy fields when the thread holds no visible link", () => {
expect(
threadPullRequestPanelTarget({ projectId: "proj-a", branchPullRequest: legacy(3) }),
).toEqual(legacy(3));
expect(
threadPullRequestPanelTarget({
projectId: "proj-a",
pullRequests: [link(4, { source: "stack-dismissed" })],
linkedPullRequest: legacy(5),
}),
).toEqual(legacy(5));
expect(threadPullRequestPanelTarget({ projectId: "proj-a", pullRequests: [] })).toBeNull();
});
});
});

describe("which actions need the host read again after they run", () => {
Expand Down
26 changes: 26 additions & 0 deletions apps/web/src/components/pullRequest/pullRequestDetail.logic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {
type VcsRef,
} from "@t3tools/contracts";
import {
resolveThreadCurrentPullRequestLink,
threadPullRequestKeysEqual,
visibleThreadPullRequests,
} from "@t3tools/shared/threadPullRequests";
Expand Down Expand Up @@ -236,6 +237,31 @@ export function pullRequestPanelContext(
: "page";
}

/**
* The pull request the right panel's "Pull request" entry opens for a thread, or null when the
* thread has none. Like `pullRequestPanelContext`, the link list decides when there is one, so a
* PR linked by an agent or the user enables the entry without the legacy field. The legacy
* fields answer only for servers that predate link lists, or a thread with only a branch PR.
*/
export function threadPullRequestPanelTarget(thread: {
readonly projectId: string;
readonly pullRequests?: ReadonlyArray<ThreadPullRequestLink> | undefined;
readonly linkedPullRequest?: ThreadLinkedPullRequest | null | undefined;
readonly branchPullRequest?: ThreadLinkedPullRequest | null | undefined;
}) {
const current = resolveThreadCurrentPullRequestLink(thread.pullRequests ?? []);
if (current !== null) {
return {
projectId: thread.projectId,
host: current.host,
repository: current.repository,
number: current.number,
url: current.url,
};
}
return thread.linkedPullRequest ?? thread.branchPullRequest ?? null;
}

/** Names where a pull-request task will land, without letting each surface guess independently. */
export function pullRequestHandoffLabels(inThisThread: boolean) {
return inThisThread
Expand Down
Loading