* 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.
98 lines
3.0 KiB
TypeScript
98 lines
3.0 KiB
TypeScript
import {
|
|
NEW_WORKSPACE_PICKER_ATTACHMENT_OWNER,
|
|
type UserComposerAttachment,
|
|
} 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" }
|
|
> & {
|
|
owner: typeof NEW_WORKSPACE_PICKER_ATTACHMENT_OWNER;
|
|
} {
|
|
return (
|
|
attachment.kind === "github_pr" && attachment.owner === NEW_WORKSPACE_PICKER_ATTACHMENT_OWNER
|
|
);
|
|
}
|
|
|
|
// Ownership lives on the attachment because drafts outlive this component.
|
|
// The picker owns at most one PR; user-added PRs and issues remain untouched.
|
|
export function syncPickerPrAttachment(input: {
|
|
attachments: UserComposerAttachment[];
|
|
item: PickerItem | null;
|
|
}): UserComposerAttachment[] {
|
|
const nextAttachments = input.attachments.filter(
|
|
(attachment) => !isPickerOwnedPrAttachment(attachment),
|
|
);
|
|
|
|
if (input.item?.kind === "github-pr") {
|
|
const selectedPr = input.item.item;
|
|
const hasExistingPrAttachment = nextAttachments.some(
|
|
(attachment) => isPrAttachment(attachment) && attachment.item.number === selectedPr.number,
|
|
);
|
|
if (!hasExistingPrAttachment) {
|
|
return [
|
|
...nextAttachments,
|
|
{
|
|
kind: "github_pr",
|
|
item: selectedPr,
|
|
owner: NEW_WORKSPACE_PICKER_ATTACHMENT_OWNER,
|
|
},
|
|
];
|
|
}
|
|
}
|
|
|
|
return nextAttachments;
|
|
}
|
|
|
|
export function clearPickerPrAttachmentForTargetChange(input: {
|
|
attachments: UserComposerAttachment[];
|
|
currentTargetId: string;
|
|
nextTargetId: string;
|
|
}): UserComposerAttachment[] {
|
|
if (input.currentTargetId === input.nextTargetId) {
|
|
return input.attachments;
|
|
}
|
|
return input.attachments.filter((attachment) => !isPrAttachment(attachment));
|
|
}
|