Close an inspected observer back to its packet on mobile (#130)

This commit is contained in:
MrAlders0n authored and GitHub committed 2026-10-01 18:46:00 -04:00
1 parent 609dbc384f
commit bdf253cbe6
3 files changed
+35 -7

No files matched your search

+10 -5
View File
@@ -274,13 +274,18 @@ function AppInner() {
// An observer opens its dashboard; a packet it was inspected from moves into the side drawer beside it.
const viewObserver = useCallback((id: string, packet?: { hash: string; observationId?: number }) => {
setPanels([]);
const next = observerDestination(searchParams, id);
const origin = new URLSearchParams(searchParams);
if (packet) {
next.set("hash", packet.hash); next.set("analyze", "1"); next.delete("path");
if (packet.observationId != null) next.set("observation", String(packet.observationId)); else next.delete("observation");
origin.set("hash", packet.hash); origin.set("analyze", "1"); origin.delete("path");
if (packet.observationId != null) origin.set("observation", String(packet.observationId)); else origin.delete("observation");
}
if (isMobile) next.delete("analyze");
navigate({ search: "?" + next.toString() });
const next = observerDestination(origin, id);
if (isMobile) {
// The dashboard covers the screen on mobile, so its Close steps back to the packet instead.
if (packet) navigate({ search: "?" + origin.toString() }, { replace: true });
next.delete("analyze");
}
navigate({ search: "?" + next.toString() }, { state: { inspected: isMobile } });
}, [navigate, searchParams, setPanels, isMobile]);
useEffect(() => {
+4 -2
View File
@@ -1,5 +1,5 @@
import { lazy, Suspense, useState, useMemo } from "react";
import { useSearchParams } from "react-router-dom";
import { useLocation, useNavigate, useSearchParams } from "react-router-dom";
import { useQuery } from "@tanstack/react-query";
import { useTranslation } from "react-i18next";
import { getBrokers } from "../../api/client";
@@ -25,6 +25,8 @@ export function ObserverPage({ wsManager, onAnalyzePacket }: { wsManager: WsMana
const { t } = useTranslation();
const now = useTick(60_000);
const id = params.get("observer");
const navigate = useNavigate();
const inspected = (useLocation().state as { inspected?: boolean } | null)?.inspected;
const [actionTime, setActionTime] = useState(() => Date.now());
const hourAt = (time: number) => Math.floor(time / 3_600_000) * 3_600_000;
const comparing = params.has("compareWith");
@@ -91,7 +93,7 @@ export function ObserverPage({ wsManager, onAnalyzePacket }: { wsManager: WsMana
<span className="font-mono text-[13px] font-medium uppercase tracking-wider text-text-dim">{t("observerPage.detail")}</span>
<div className="-mr-1 flex items-center gap-0.5">
<MinimizeButton collapsed={minimized} onToggle={() => setMinimized(v => !v)} />
<CloseButton onClose={() => select(null)} label={t("observerPage.back")} />
<CloseButton onClose={() => inspected ? navigate(-1) : select(null)} label={t("observerPage.back")} />
</div>
</div>
<div className={`min-h-0 flex-1 overflow-auto ${minimized ? "hidden md:block" : ""}`}>
+21
View File
@@ -164,3 +164,24 @@ describe("observer investigation windows", () => {
}
});
});
describe("observer investigation on mobile", () => {
beforeEach(() => vi.stubGlobal("matchMedia", (query: string) => ({ matches: /max-width/.test(query), media: query, addEventListener: () => {}, removeEventListener: () => {}, addListener: () => {}, removeListener: () => {} })));
it("closes an inspected observer back to the packet it came from", async () => {
render(<App />); click("Route packet"); click("Select report 8"); click("Packet observer");
expect(await screen.findByRole("heading", { level: 1 })).toHaveTextContent("Dashboard o1");
expect(screen.queryByRole("heading", { name: /^Packet / })).not.toBeInTheDocument();
click("Back to observers");
await waitFor(() => expect(new URLSearchParams(window.location.search).get("tab")).toBe("Routes"));
expect(await screen.findByRole("heading", { name: /^Packet / })).toHaveTextContent("Packet aa");
expect(new URLSearchParams(window.location.search).get("observation")).toBe("8");
});
it("closes a dashboard opened from the list back to the list", async () => {
window.history.replaceState({}, "", "/?tab=Observers&observer=o1");
render(<App />);
await screen.findByRole("heading", { level: 1 });
click("Back to observers");
await waitFor(() => expect(screen.queryByRole("heading", { level: 1 })).not.toBeInTheDocument());
expect(new URLSearchParams(window.location.search).get("tab")).toBe("Observers");
});
});