diff --git a/frontend/src/App.test.tsx b/frontend/src/App.test.tsx index 1fb7233fd..05a77c5ce 100644 --- a/frontend/src/App.test.tsx +++ b/frontend/src/App.test.tsx @@ -2253,13 +2253,17 @@ describe("App, authenticated", () => { "no_comparison_group" as const, "No other visible posts share this comparison group yet. Request reconstruction after more posts arrive, or read Keyman and evaluation.", ], - ])("explains an empty focused Event Lineage graph: %s", async (lineageIsolationReason, message) => { - stubBackend({ lineageIsolationReason }); - render(); - await userEvent.click(await screen.findByRole("button", { name: "View post: Public post" })); - expect(await screen.findByText(message)).toBeInTheDocument(); - expect(screen.queryByText("No linked posts yet.")).not.toBeInTheDocument(); - }); + ])( + "explains an empty focused Event Lineage graph: %s", + async (lineageIsolationReason, message) => { + stubBackend({ lineageIsolationReason }); + render(); + await userEvent.click(await screen.findByRole("button", { name: "View post: Public post" })); + expect(await screen.findByText(message)).toBeInTheDocument(); + expect(screen.queryByText("No linked posts yet.")).not.toBeInTheDocument(); + }, + 15_000, + ); it("shows an embedded invoice image instead of the raw base64 string", async () => { const tinyPng = diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 1c3b53ed2..4ea82a341 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1,6 +1,7 @@ import { AdminPanel } from "./components/AdminPanel"; import { LeftoverPairList } from "./components/LeftoverPairList"; import { WorkspaceCalendar } from "./components/WorkspaceCalendar"; +import { focusedGraphMustReset } from "./focusedGraphSelection"; import { useCallback, useEffect, useRef, useState, type ReactNode } from "react"; import { useAuth } from "react-oidc-context"; @@ -3823,7 +3824,6 @@ function PostList({ onPostOpened?: () => void; }) { const [posts, setPosts] = useState(null); - const [graph, setGraph] = useState(null); const [focusedGraph, setFocusedGraph] = useState(null); const [error, setError] = useState(null); const [selectedPostId, setSelectedPostId] = useState(null); @@ -3886,8 +3886,10 @@ function PostList({ } function selectPost(postId: string, options?: SelectPostOptions) { + if (focusedGraphMustReset(selectedPostId, postId)) { + setFocusedGraph(null); + } setSelectedPostId(postId); - setFocusedGraph(null); setOpenedAfterCutoff(Boolean(options?.liveAfterCutoff)); setOpenedCutoffIso(options?.knowledgeCutoff ?? null); setOpenedFromReportMember(Boolean(options?.fromReportMember)); @@ -3955,7 +3957,6 @@ function PostList({ }, [loadPostPage]); useEffect(() => { - fetchLineageGraph(accessToken).then(setGraph).catch(() => setGraph({ nodes: [], edges: [] })); fetchMe(accessToken) .then((me) => { setCanRebuild(me.permission_codes.includes("post_admin")); @@ -3974,6 +3975,7 @@ function PostList({ setFocusedGraph(null); return; } + setFocusedGraph(null); let active = true; fetchLineageGraph(accessToken, selectedPostId) .then((nextGraph) => { @@ -3992,7 +3994,6 @@ function PostList({ setRebuildError(null); try { await rebuildLineage(accessToken); - setGraph(await fetchLineageGraph(accessToken)); } catch (err) { setRebuildError(String(err)); } finally { @@ -4304,7 +4305,7 @@ function PostList({ postId={selectedPostId} accessToken={accessToken} canExtract={canRebuild} - graph={focusedGraph ?? graph} + graph={focusedGraph} liveBodyWarning={ openedAfterCutoff ? analysisRunOpenedBodyWarning(openedCutoffIso) : null } diff --git a/frontend/src/components/WorkspaceCalendar.tsx b/frontend/src/components/WorkspaceCalendar.tsx index 5f2631f39..0b37ae2dc 100644 --- a/frontend/src/components/WorkspaceCalendar.tsx +++ b/frontend/src/components/WorkspaceCalendar.tsx @@ -33,7 +33,7 @@ export function WorkspaceCalendar({

{t("Observed calendar events")}

{events.length === 0 ? ( -

+

{naruonAvailable ? t("No observed calendar events are available.") : failClosedCopy} diff --git a/frontend/src/focusedGraphSelection.test.ts b/frontend/src/focusedGraphSelection.test.ts new file mode 100644 index 000000000..f8522cf67 --- /dev/null +++ b/frontend/src/focusedGraphSelection.test.ts @@ -0,0 +1,9 @@ +import { describe, expect, it } from "vitest"; +import { focusedGraphMustReset } from "./focusedGraphSelection"; + +describe("focusedGraphMustReset", () => { + it("retains a loaded graph when the already-open post is selected again", () => { + expect(focusedGraphMustReset("post-1", "post-1")).toBe(false); + expect(focusedGraphMustReset("post-1", "post-2")).toBe(true); + }); +}); diff --git a/frontend/src/focusedGraphSelection.ts b/frontend/src/focusedGraphSelection.ts new file mode 100644 index 000000000..286e4eb47 --- /dev/null +++ b/frontend/src/focusedGraphSelection.ts @@ -0,0 +1,4 @@ +/** Return whether selecting a post requires discarding the currently focused graph. */ +export function focusedGraphMustReset(currentPostId: string | null, nextPostId: string): boolean { + return currentPostId !== nextPostId; +}