Auto-attach pasted GitHub PR/issue URLs in the composer (#1006)
* Auto-attach pasted GitHub PR/issue URLs in the composer Paste a GitHub PR or issue link into any composer and it shows up as an attachment pill within a debounce tick when the URL matches the workspace's remote. On the new-workspace screen, an attached PR also surfaces an inline "Check out PR #N?" hint next to the picker; one click promotes it to the checkout target through the same syncPickerPrAttachment flow the dropdown uses. Unifies the new-workspace picker and composer attachment picker on one shared GitHub search hook and cache key, so both consumers (and the auto-attach lookup) share the React Query cache. * Fix oxfmt formatting in new-workspace-screen import
This commit is contained in:
@@ -0,0 +1,42 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { buildGithubSearchQueryOptions, githubSearchQueryKey } from "./use-github-search-query";
|
||||
|
||||
describe("githubSearchQueryKey", () => {
|
||||
it("keeps the shared cache key shape for no-kinds searches", () => {
|
||||
expect(githubSearchQueryKey("server-1", "/repo", " 123 ")).toEqual([
|
||||
"github-search",
|
||||
"server-1",
|
||||
"/repo",
|
||||
"123",
|
||||
]);
|
||||
});
|
||||
|
||||
it("adds a deterministic kinds key when kinds are specified", () => {
|
||||
expect(githubSearchQueryKey("server-1", "/repo", "123", ["github-pr", "github-issue"])).toEqual(
|
||||
["github-search", "server-1", "/repo", "123", "github-issue,github-pr"],
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("buildGithubSearchQueryOptions", () => {
|
||||
it("forwards kinds to the GitHub search request when specified", async () => {
|
||||
const requests: unknown[] = [];
|
||||
const query = buildGithubSearchQueryOptions({
|
||||
client: {
|
||||
async searchGitHub(options) {
|
||||
requests.push(options);
|
||||
return { items: [], githubFeaturesEnabled: true, error: null, requestId: "request-1" };
|
||||
},
|
||||
},
|
||||
serverId: "server-1",
|
||||
cwd: "/repo",
|
||||
query: " 123 ",
|
||||
kinds: ["github-pr"],
|
||||
enabled: true,
|
||||
});
|
||||
|
||||
await query.queryFn();
|
||||
|
||||
expect(requests).toEqual([{ cwd: "/repo", query: "123", limit: 20, kinds: ["github-pr"] }]);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,64 @@
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import type { GitHubSearchRequest, GitHubSearchResponse } from "@server/shared/messages";
|
||||
|
||||
export const GITHUB_SEARCH_STALE_TIME = 30_000;
|
||||
|
||||
export type GitHubSearchPayload = GitHubSearchResponse["payload"];
|
||||
|
||||
export interface GitHubSearchClient {
|
||||
searchGitHub: (
|
||||
options: {
|
||||
cwd: string;
|
||||
query: string;
|
||||
limit?: number;
|
||||
kinds?: GitHubSearchRequest["kinds"];
|
||||
},
|
||||
requestId?: string,
|
||||
) => Promise<GitHubSearchPayload>;
|
||||
}
|
||||
|
||||
interface GitHubSearchQueryInput {
|
||||
client: GitHubSearchClient | null;
|
||||
serverId: string;
|
||||
cwd: string;
|
||||
query: string;
|
||||
kinds?: GitHubSearchRequest["kinds"];
|
||||
enabled: boolean;
|
||||
}
|
||||
|
||||
export function githubSearchQueryKey(
|
||||
serverId: string,
|
||||
cwd: string,
|
||||
query: string,
|
||||
kinds?: GitHubSearchRequest["kinds"],
|
||||
) {
|
||||
const trimmedQuery = query.trim();
|
||||
if (!kinds) {
|
||||
return ["github-search", serverId, cwd, trimmedQuery] as const;
|
||||
}
|
||||
return ["github-search", serverId, cwd, trimmedQuery, [...kinds].sort().join(",")] as const;
|
||||
}
|
||||
|
||||
export function buildGithubSearchQueryOptions(input: GitHubSearchQueryInput) {
|
||||
const query = input.query.trim();
|
||||
|
||||
return {
|
||||
queryKey: githubSearchQueryKey(input.serverId, input.cwd, query, input.kinds),
|
||||
queryFn: async (): Promise<GitHubSearchPayload> => {
|
||||
if (!input.client) {
|
||||
throw new Error("Host is not connected");
|
||||
}
|
||||
const request = { cwd: input.cwd, query, limit: 20 };
|
||||
if (input.kinds) {
|
||||
return input.client.searchGitHub({ ...request, kinds: input.kinds });
|
||||
}
|
||||
return input.client.searchGitHub(request);
|
||||
},
|
||||
enabled: input.enabled && Boolean(input.client),
|
||||
staleTime: GITHUB_SEARCH_STALE_TIME,
|
||||
};
|
||||
}
|
||||
|
||||
export function useGithubSearchQuery(input: GitHubSearchQueryInput) {
|
||||
return useQuery(buildGithubSearchQueryOptions(input));
|
||||
}
|
||||
Reference in New Issue
Block a user