From d6bc201f4628aae56b47690b96757d09bdcb2271 Mon Sep 17 00:00:00 2001 From: Mohamed Boudra Date: Fri, 17 Jul 2026 18:10:55 +0200 Subject: [PATCH] fix(app): refresh import availability previews --- .../src/project-config-import/availability.ts | 27 ++++++- .../project-config-import/preview-cache.ts | 2 +- .../project-config-import-model.test.ts | 74 +++++++++++++++++++ 3 files changed, 98 insertions(+), 5 deletions(-) diff --git a/packages/app/src/project-config-import/availability.ts b/packages/app/src/project-config-import/availability.ts index 858e460bb..6e47208d0 100644 --- a/packages/app/src/project-config-import/availability.ts +++ b/packages/app/src/project-config-import/availability.ts @@ -1,5 +1,14 @@ +import type { + ProjectConfigImportPreview, + ProjectConfigRpcError, +} from "@getpaseo/protocol/messages"; + export type ProjectConfigImportAvailabilityStatus = "loading" | "none" | "one" | "many"; +type ProjectConfigImportOpenablePreview = + | ({ ok: true } & Pick) + | { ok: false; error: Pick }; + export function projectConfigImportAvailabilityStatus(input: { availableCount: number; isLoading: boolean; @@ -14,9 +23,19 @@ export function projectConfigImportAvailabilityStatus(input: { } export function projectConfigImportPreviewIsOpenable( - preview: { ok: true; status: string } | { ok: false; error: { code: string } } | null | undefined, + preview: ProjectConfigImportOpenablePreview | null | undefined, ): boolean { - return preview?.ok === true - ? preview.status === "available" - : preview?.error.code === "invalid_source_config"; + if (!preview) { + return false; + } + if (!preview.ok) { + return preview.error.code === "invalid_source_config"; + } + if (preview.status === "available") { + return true; + } + if (preview.status === "nothing_to_import") { + return preview.items.length > 0; + } + return false; } diff --git a/packages/app/src/project-config-import/preview-cache.ts b/packages/app/src/project-config-import/preview-cache.ts index 3a1b0146d..99413eaab 100644 --- a/packages/app/src/project-config-import/preview-cache.ts +++ b/packages/app/src/project-config-import/preview-cache.ts @@ -39,7 +39,7 @@ export function projectConfigImportPreviewQueryInput(input: { enabled: input.enabled && Boolean(input.client && input.serverId && input.repoRoot && input.protocolSource), - refetchOnMount: false, + refetchOnMount: true, retry: false, staleTimeMs: PROJECT_CONFIG_IMPORT_PREVIEW_STALE_MS, dataShape: "value", diff --git a/packages/app/src/project-config-import/project-config-import-model.test.ts b/packages/app/src/project-config-import/project-config-import-model.test.ts index 327da1893..3203b6734 100644 --- a/packages/app/src/project-config-import/project-config-import-model.test.ts +++ b/packages/app/src/project-config-import/project-config-import-model.test.ts @@ -217,6 +217,61 @@ describe("project config import preview cache keys", () => { ); }); + it("refetches a stale availability preview when the settings page remounts", () => { + const calls: string[] = []; + const client = { + getProjectConfigImport: async (): Promise< + ProjectConfigImportPreview & { ok: true; requestId: string } + > => { + calls.push("preview"); + return { + ok: true, + requestId: "preview-current", + repoRoot: "/repo", + source: protocolSource, + status: "available", + sourceRevision: "source-current", + paseoRevision: null, + inputs: [], + items: [], + preview: {}, + }; + }, + }; + const input = projectConfigImportPreviewQueryInput({ + client, + serverId: "server", + repoRoot: "/repo", + source: fakeSource, + protocolSource, + enabled: true, + }); + const queryClient = new QueryClient(); + queryClient.setQueryData( + input.queryKey, + { + ok: true, + requestId: "preview-stale", + repoRoot: "/repo", + source: protocolSource, + status: "not_found", + sourceRevision: null, + paseoRevision: null, + inputs: [], + items: [], + preview: null, + }, + { updatedAt: Date.now() - 6_000 }, + ); + const options = fetchQueryOptions(input); + const observer = new QueryObserver(queryClient, queryClient.defaultQueryOptions(options)); + + const unsubscribe = observer.subscribe(() => {}); + + expect(calls).toEqual(["preview"]); + unsubscribe(); + }); + it("keeps advertised identity separate from the protocol source after opening", async () => { const sameKindRegistry = createProjectConfigImportSourceRegistry([ { @@ -319,4 +374,23 @@ describe("project config import availability", () => { }), ).toBe(false); }); + + it("keeps warning-only advertised sources openable", () => { + expect( + projectConfigImportPreviewIsOpenable({ + ok: true, + status: "nothing_to_import", + items: [ + { key: "environment_variables", label: "Environment variables", outcome: "unsupported" }, + ], + }), + ).toBe(true); + expect( + projectConfigImportPreviewIsOpenable({ + ok: true, + status: "nothing_to_import", + items: [], + }), + ).toBe(false); + }); });