Start new workspaces from pasted pull requests (#2290)
* fix(workspaces): start pasted pull requests from their branch Recognized pull request links now select their branch directly instead of waiting for a separate confirmation. Ensure single-branch clones also record the fetched branch so the new worktree can track it. * fix(workspaces): keep pasted PR checkout target-safe * fix(workspaces): make pasted PR resolution deterministic * fix(workspaces): preserve explicit checkout selection A recognized pull request selects itself only when its attachment is added. Once the user chooses a branch, later composer edits must not derive checkout state from existing attachments. * fix(composer): ignore stale pull request lookups A lookup may finish after the workspace target changes. Only apply its attachment and selection event when the host, working directory, client, and remote still match the target that started it. * fix(workspaces): make checkout choices authoritative Arm automatic pull request selection only when a new PR link is detected. An explicit picker choice cancels that pending selection, existing draft attachments never infer checkout state, and target changes suppress carried-over lookups. * fix(workspaces): stabilize pull request auto-selection Keep the first PR selected when one edit contains several links, and accept a completed lookup when only the transport client changed for the same host and checkout. * fix(composer): release removed pull request lookups Release each auto-attach invocation when its composer state is abandoned, while continuing to discard any stale response.
This commit is contained in:
@@ -4,6 +4,46 @@ import {
|
||||
} from "@/attachments/types";
|
||||
import type { PickerItem } from "./new-workspace-picker-item";
|
||||
|
||||
export interface PickerSelectionState {
|
||||
selectedItem: PickerItem | null;
|
||||
allowAutoPrSelection: boolean;
|
||||
}
|
||||
|
||||
export type PickerSelectionEvent =
|
||||
| { type: "pr-detected" }
|
||||
| { type: "pr-added"; item: Extract<PickerItem, { kind: "github-pr" }> }
|
||||
| { type: "picker-selected"; item: PickerItem }
|
||||
| { type: "target-changed" };
|
||||
|
||||
export const initialPickerSelectionState: PickerSelectionState = {
|
||||
selectedItem: null,
|
||||
allowAutoPrSelection: false,
|
||||
};
|
||||
|
||||
export function reducePickerSelection(
|
||||
state: PickerSelectionState,
|
||||
event: PickerSelectionEvent,
|
||||
): PickerSelectionState {
|
||||
switch (event.type) {
|
||||
case "pr-detected":
|
||||
return { ...state, allowAutoPrSelection: true };
|
||||
case "pr-added":
|
||||
return state.allowAutoPrSelection
|
||||
? { selectedItem: event.item, allowAutoPrSelection: false }
|
||||
: state;
|
||||
case "picker-selected":
|
||||
return { selectedItem: event.item, allowAutoPrSelection: false };
|
||||
case "target-changed":
|
||||
return initialPickerSelectionState;
|
||||
}
|
||||
}
|
||||
|
||||
function isPrAttachment(
|
||||
attachment: UserComposerAttachment,
|
||||
): attachment is Extract<UserComposerAttachment, { kind: "forge_change_request" | "github_pr" }> {
|
||||
return attachment.kind === "forge_change_request" || attachment.kind === "github_pr";
|
||||
}
|
||||
|
||||
function isPickerOwnedPrAttachment(attachment: UserComposerAttachment): attachment is Extract<
|
||||
UserComposerAttachment,
|
||||
{ kind: "github_pr" }
|
||||
@@ -28,8 +68,7 @@ export function syncPickerPrAttachment(input: {
|
||||
if (input.item?.kind === "github-pr") {
|
||||
const selectedPr = input.item.item;
|
||||
const hasExistingPrAttachment = nextAttachments.some(
|
||||
(attachment) =>
|
||||
attachment.kind === "github_pr" && attachment.item.number === selectedPr.number,
|
||||
(attachment) => isPrAttachment(attachment) && attachment.item.number === selectedPr.number,
|
||||
);
|
||||
if (!hasExistingPrAttachment) {
|
||||
return [
|
||||
@@ -54,24 +93,5 @@ export function clearPickerPrAttachmentForTargetChange(input: {
|
||||
if (input.currentTargetId === input.nextTargetId) {
|
||||
return input.attachments;
|
||||
}
|
||||
return syncPickerPrAttachment({ attachments: input.attachments, item: null });
|
||||
}
|
||||
|
||||
export function findCheckoutHintPrAttachment(input: {
|
||||
attachments: ReadonlyArray<UserComposerAttachment>;
|
||||
selectedItem: PickerItem | null;
|
||||
dismissedPrNumbers: ReadonlySet<number>;
|
||||
}): Extract<UserComposerAttachment, { kind: "github_pr" }> | null {
|
||||
const selectedPrNumber =
|
||||
input.selectedItem?.kind === "github-pr" ? input.selectedItem.item.number : null;
|
||||
|
||||
for (const attachment of input.attachments) {
|
||||
if (attachment.kind !== "github_pr") continue;
|
||||
const prNumber = attachment.item.number;
|
||||
if (prNumber === selectedPrNumber) continue;
|
||||
if (input.dismissedPrNumbers.has(prNumber)) continue;
|
||||
return attachment;
|
||||
}
|
||||
|
||||
return null;
|
||||
return input.attachments.filter((attachment) => !isPrAttachment(attachment));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user