fix(web): leave chat with a document load for chart, ephemeris, and reports
Independent Staging Quality Gate / validate (pull_request) Failing after 9m45s
Independent Staging Quality Gate / publish (pull_request) Skipped
Independent Staging Quality Gate / validate (push) Failing after 9m14s
Independent Staging Quality Gate / publish (push) Skipped

Session URLs are written with history.pushState, so App Router
router.push cannot leave the home page. Sidebar destinations now
assign the document and stash the current conversation for return.
This commit is contained in:
jesse-ux
2026-09-15 19:29:41 +08:00
parent 298a73e8e5
commit c6ecb86fb4
3 changed files with 26 additions and 9 deletions
+11 -6
View File
@@ -18,9 +18,10 @@ import {
UserRound,
Users,
} from "lucide-react";
import { usePathname, useRouter } from "next/navigation";
import { usePathname } from "next/navigation";
import { useEffect, useRef } from "react";
import type { Ref } from "react";
import { persistLoginSessionReturn } from "@/lib/chat-session-url";
import {
Sidebar,
SidebarContent,
@@ -102,7 +103,7 @@ export function AppSidebar({
sessionControls,
onAccountMenuOpenChange,
onNewChat,
onOpenReports,
onOpenReports: _onOpenReports,
onSelectSession,
onSelectChart,
onAddChart,
@@ -113,7 +114,6 @@ export function AppSidebar({
onOpenLogout,
}: AppSidebarProps) {
const { isMobile, setOpen, setOpenMobile, state, viewport } = useSidebar();
const router = useRouter();
const pathname = usePathname();
const firstSessionRef = useRef<HTMLButtonElement>(null);
const historyHeadingRef = useRef<HTMLElement>(null);
@@ -149,18 +149,23 @@ export function AppSidebar({
if (isMobile) setOpenMobile(false);
}
function leaveChat(path: "/chart" | "/ephemeris" | "/reports") {
persistLoginSessionReturn();
window.location.assign(path);
}
function handleOpenReports() {
onOpenReports();
leaveChat("/reports");
if (isMobile) setOpenMobile(false);
}
function handleOpenChart() {
router.push("/chart");
leaveChat("/chart");
if (isMobile) setOpenMobile(false);
}
function handleOpenEphemeris() {
router.push("/ephemeris");
leaveChat("/ephemeris");
if (isMobile) setOpenMobile(false);
}
@@ -27,15 +27,15 @@ test("in-app destinations navigate client-side so the chat survives the round tr
assert.match(pageSource, /const router = useRouter\(\)/);
// 原值: 4 条 membershipHref 软跳 /membership
// 新值: 账单入口改 openAccountDialog("billing")报告仍 router.push
// 原因: 删除 /membership 整页,对话状态不丢
// 新值: 账单入口改 openAccountDialog("billing")离开对话的星盘/星历/报告走 document load
// 原因: 对话用 history.pushState 写 ?c=App Router 的 router.push 无法离开首页
assert.match(pageSource, /onOpenReports=\{\(\) => router\.push\("\/reports"\)\}/);
assert.match(pageSource, /onOpenBilling=\{\(\) => openAccountDialog\("billing", \{ source: "account-menu" \}\)\}/);
assert.match(pageSource, /openAccountDialog\("billing", \{ returnTarget: event\.currentTarget, source: "credits" \}\)/);
assert.match(pageSource, /openAccountDialog\("billing", \{ source: "insufficient-credits" \}\)/);
assert.match(pageSource, /if \(response\.status === 402\) openAccountDialog\("billing", \{ source: "insufficient-credits" \}\)/);
// And: none of them fall back to a document load.
// And: membership stays in-page; leaving chat uses a document load in the sidebar.
assert.doesNotMatch(pageSource, /window\.location\.assign\("\/reports"\)/);
assert.doesNotMatch(pageSource, /window\.location\.assign\(membershipHref\(/);
});
+12
View File
@@ -185,6 +185,18 @@ test("keeps the sidebar brand row free of a duplicate collapse trigger", () => {
assert.doesNotMatch(appSidebar, /SidebarTrigger/);
});
test("leaves the chat document for chart, ephemeris, and reports", () => {
const appSidebar = readProjectFile("src/components/app-sidebar.tsx");
// 对话首页用 history.pushState 维护 ?c=App Router 的 router.push 无法离开。
assert.match(appSidebar, /persistLoginSessionReturn\(\);\s*window\.location\.assign\(path\)/);
assert.match(appSidebar, /leaveChat\("\/chart"\)/);
assert.match(appSidebar, /leaveChat\("\/ephemeris"\)/);
assert.match(appSidebar, /leaveChat\("\/reports"\)/);
assert.doesNotMatch(appSidebar, /router\.push\("\/chart"\)/);
assert.doesNotMatch(appSidebar, /router\.push\("\/ephemeris"\)/);
assert.doesNotMatch(appSidebar, /useRouter/);
});
test("uses one collapsed history action instead of icon-only session rows", () => {
const appSidebar = readProjectFile("src/components/app-sidebar.tsx");
assert.match(appSidebar, /MessageSquareText/);