fix(usage): only list apps shown on the Apps page in the app filter

The usage dashboard listed every known app as a filter chip regardless of
the visibility switches on the Apps page. Filter the chips the same way the
session manager does: Claude Code stays while Claude Desktop is shown, and
the currently selected app is kept even if it has been hidden.
This commit is contained in:
Jason committed 2026-10-21 17:04:27 +08:00
1 parent 92ec18d774
commit 711a4127ff
2 files changed
+52 -1

No files matched your search

+15 -1
View File
@@ -39,6 +39,8 @@ import {
useProviderStats,
useSessionUsageLastSync,
} from "@/lib/query/usage";
import { useSettingsQuery } from "@/lib/query";
import { DEFAULT_VISIBLE_APPS } from "@/config/appConfig";
import { useUsageEventBridge } from "@/hooks/useUsageEventBridge";
import { useUsageAutoRefresh } from "@/hooks/useUsageAutoRefresh";
import { usageApi } from "@/lib/api/usage";
@@ -516,6 +518,18 @@ export function UsageDashboard({
// ── 筛选行 ───────────────────────────────────────────────────────────
// 选中块是单独的滑块(SegmentThumb),切换应用时滑过去
// 只列「应用」页设为显示的应用;Claude Code 的用量在 Claude Desktop 显示时也算,
// 当前选中的即使被隐藏也留着,免得筛选状态无处可见
const { data: settings } = useSettingsQuery();
const filterApps = useMemo(() => {
const visible = { ...DEFAULT_VISIBLE_APPS, ...settings?.visibleApps };
return KNOWN_APP_TYPES.filter(
(app) =>
visible[app] ||
(app === "claude" && visible["claude-desktop"]) ||
app === appType,
);
}, [settings?.visibleApps, appType]);
const appFilterIndicator = useSlidingIndicator<HTMLDivElement>(
'[aria-pressed="true"]',
appType,
@@ -550,7 +564,7 @@ export function UsageDashboard({
{t("usage.appFilter.all")}
</button>
{/* 应用只露图标,名字放悬停提示和 aria-label */}
{KNOWN_APP_TYPES.map((app) => (
{filterApps.map((app) => (
<HoverTip key={app} content={APP_DISPLAY_NAME[app]}>
<button
type="button"
+37
View File
@@ -8,6 +8,7 @@ import {
resetUsageSyncClockForTests,
} from "@/components/usage/UsageDashboard";
import type { UsageRangeSelection } from "@/types/usage";
import { setSettings } from "../msw/state";
const useProviderStatsMock = vi.hoisted(() => vi.fn());
const useModelStatsMock = vi.hoisted(() => vi.fn());
@@ -324,6 +325,42 @@ describe("UsageDashboard", () => {
);
});
it("lists only the apps shown on the Apps page, keeping the selected one", async () => {
setSettings({
visibleApps: {
claude: false,
"claude-desktop": true,
codex: true,
gemini: false,
grokbuild: false,
opencode: false,
openclaw: true,
hermes: true,
pi: false,
mcode: false,
agy: false,
},
});
renderDashboard({ initialAppType: "gemini" });
await waitFor(() =>
expect(
screen.queryByRole("button", { name: "Pi" }),
).not.toBeInTheDocument(),
);
// Claude Desktop 显示时 Claude Code 的用量也列;选中的 Gemini 虽被隐藏也留着
expect(
screen.getByRole("button", { name: "Claude Code" }),
).toBeInTheDocument();
expect(screen.getByRole("button", { name: /Codex/ })).toBeInTheDocument();
expect(
screen.getByRole("button", { name: /Gemini/, pressed: true }),
).toBeInTheDocument();
expect(
screen.queryByRole("button", { name: /Grok/ }),
).not.toBeInTheDocument();
});
it("filters by provider and lists request counts", async () => {
const user = userEvent.setup();
renderDashboard();