fix(app): auto-set ref picker from a single attached PR (#951)
* fix(app): auto-set ref picker from a single attached PR Attaching a PR in the new-workspace composer signals "I want this PR's context", and almost always also means "base the worktree on this PR". Until now the two paths were independent: the ref picker stayed on the default branch, so the worktree was branched off main even though the sidebar chip looked like a PR checkout. Auto-promote the picker when exactly one PR is attached and the user hasn't manually picked a ref. Manual picker selections are preserved; auto-promoted ones are cleared when their backing attachment is removed. * refactor(app): derive auto-promoted picker selection instead of syncing The previous version drove `pickerSelection` from a useEffect that inspected attachments and overwrote state. That meant the shared `PickerSelection` type had to grow a `source: "manual" | "attachment"` discriminator just so the effect could tell its own writes apart from manual picks. Classic effect-as-derived-state. Split the state instead. `manualPickerSelection` holds explicit picks; `autoPickerItem` is a useMemo over composer attachments; the effective `pickerSelection` is `manual ?? auto` via a small precedence helper. No effect, no `source` flag, and `PickerSelection` stays a screen-local shape since nothing outside needs it.
This commit is contained in:
@@ -1,7 +1,10 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import type { UserComposerAttachment } from "@/attachments/types";
|
||||
import type { GitHubSearchItem } from "@server/shared/messages";
|
||||
import { syncPickerPrAttachment } from "./new-workspace-picker-state";
|
||||
import {
|
||||
deriveAutoPickerItemFromAttachments,
|
||||
syncPickerPrAttachment,
|
||||
} from "./new-workspace-picker-state";
|
||||
|
||||
function makePrItem(number: number, title: string, headRefName = "feature/x"): GitHubSearchItem {
|
||||
return {
|
||||
@@ -94,3 +97,35 @@ describe("syncPickerPrAttachment", () => {
|
||||
expect(result.attachments).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe("deriveAutoPickerItemFromAttachments", () => {
|
||||
it("returns null when there are no attachments", () => {
|
||||
expect(deriveAutoPickerItemFromAttachments([])).toBeNull();
|
||||
});
|
||||
|
||||
it("returns the PR when exactly one is attached", () => {
|
||||
const pr = makePrItem(923, "Nix overridable npm deps hash");
|
||||
expect(deriveAutoPickerItemFromAttachments([prAttachment(pr)])).toEqual({
|
||||
kind: "github-pr",
|
||||
item: pr,
|
||||
});
|
||||
});
|
||||
|
||||
it("returns null when multiple PRs are attached", () => {
|
||||
const a = makePrItem(101, "A");
|
||||
const b = makePrItem(202, "B");
|
||||
expect(deriveAutoPickerItemFromAttachments([prAttachment(a), prAttachment(b)])).toBeNull();
|
||||
});
|
||||
|
||||
it("ignores non-PR attachments", () => {
|
||||
expect(deriveAutoPickerItemFromAttachments([issueAttachment(44)])).toBeNull();
|
||||
});
|
||||
|
||||
it("returns the lone PR even when other non-PR attachments are present", () => {
|
||||
const pr = makePrItem(923, "Nix overridable npm deps hash");
|
||||
expect(deriveAutoPickerItemFromAttachments([issueAttachment(44), prAttachment(pr)])).toEqual({
|
||||
kind: "github-pr",
|
||||
item: pr,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user