mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-07-24 12:44:18 +08:00
fix(usage): persist dashboard refresh interval (#5057)
This commit is contained in:
@@ -507,7 +507,12 @@ export function SettingsPage({
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="usage" className="mt-0">
|
||||
<UsageDashboard />
|
||||
<UsageDashboard
|
||||
refreshIntervalMs={settings?.usageDashboardRefreshIntervalMs}
|
||||
onRefreshIntervalChange={(usageDashboardRefreshIntervalMs) =>
|
||||
handleAutoSave({ usageDashboardRefreshIntervalMs })
|
||||
}
|
||||
/>
|
||||
</TabsContent>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useMemo, useState } from "react";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { UsageHero } from "./UsageHero";
|
||||
import { UsageTrendChart } from "./UsageTrendChart";
|
||||
@@ -46,8 +46,17 @@ import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||
|
||||
const APP_FILTER_OPTIONS: AppTypeFilter[] = ["all", ...KNOWN_APP_TYPES];
|
||||
|
||||
// 0 表示关闭自动刷新(refetchInterval=false)
|
||||
const DEFAULT_REFRESH_INTERVAL_MS = 30000;
|
||||
const REFRESH_INTERVAL_OPTIONS_MS = [0, 5000, 10000, 30000, 60000] as const;
|
||||
type RefreshIntervalOption = (typeof REFRESH_INTERVAL_OPTIONS_MS)[number];
|
||||
|
||||
const isRefreshIntervalOption = (
|
||||
value: number | undefined,
|
||||
): value is RefreshIntervalOption =>
|
||||
REFRESH_INTERVAL_OPTIONS_MS.includes(value as RefreshIntervalOption);
|
||||
|
||||
const normalizeRefreshInterval = (value: number | undefined) =>
|
||||
isRefreshIntervalOption(value) ? value : DEFAULT_REFRESH_INTERVAL_MS;
|
||||
|
||||
// 与 AppSwitcher 的 appIconName 保持一致(codex 复用 openai 图标)
|
||||
const APP_FILTER_ICON: Record<AppType, string> = {
|
||||
@@ -64,7 +73,15 @@ const encodeOptionValue = (name: string) => `${DYNAMIC_OPTION_PREFIX}${name}`;
|
||||
const decodeOptionValue = (value: string) =>
|
||||
value === "all" ? undefined : value.slice(DYNAMIC_OPTION_PREFIX.length);
|
||||
|
||||
export function UsageDashboard() {
|
||||
interface UsageDashboardProps {
|
||||
refreshIntervalMs?: number;
|
||||
onRefreshIntervalChange?: (next: number) => Promise<boolean> | boolean | void;
|
||||
}
|
||||
|
||||
export function UsageDashboard({
|
||||
refreshIntervalMs: savedRefreshIntervalMs,
|
||||
onRefreshIntervalChange,
|
||||
}: UsageDashboardProps = {}) {
|
||||
const { t, i18n } = useTranslation();
|
||||
const queryClient = useQueryClient();
|
||||
const [range, setRange] = useState<UsageRangeSelection>({ preset: "today" });
|
||||
@@ -73,7 +90,13 @@ export function UsageDashboard() {
|
||||
undefined,
|
||||
);
|
||||
const [model, setModel] = useState<string | undefined>(undefined);
|
||||
const [refreshIntervalMs, setRefreshIntervalMs] = useState(30000);
|
||||
const [refreshIntervalMs, setRefreshIntervalMs] = useState(() =>
|
||||
normalizeRefreshInterval(savedRefreshIntervalMs),
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
setRefreshIntervalMs(normalizeRefreshInterval(savedRefreshIntervalMs));
|
||||
}, [savedRefreshIntervalMs]);
|
||||
|
||||
// 切应用时清掉下游筛选,避免留下一个在新范围内查无数据的"幽灵"组合;
|
||||
// 切 Provider 同理清掉模型(模型选项随 Provider 级联)。
|
||||
@@ -95,9 +118,23 @@ export function UsageDashboard() {
|
||||
// usage 查询,实现实时刷新(仅在 Dashboard 挂载时生效,离开页面自动取消监听)
|
||||
useUsageEventBridge();
|
||||
|
||||
const changeRefreshInterval = (next: number) => {
|
||||
setRefreshIntervalMs(next);
|
||||
const changeRefreshInterval = async (next: number) => {
|
||||
const normalized = normalizeRefreshInterval(next);
|
||||
const previous = refreshIntervalMs;
|
||||
setRefreshIntervalMs(normalized);
|
||||
queryClient.invalidateQueries({ queryKey: usageKeys.all });
|
||||
try {
|
||||
const saved = await onRefreshIntervalChange?.(normalized);
|
||||
if (saved === false) {
|
||||
setRefreshIntervalMs(previous);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(
|
||||
"[UsageDashboard] Failed to persist refresh interval",
|
||||
error,
|
||||
);
|
||||
setRefreshIntervalMs(previous);
|
||||
}
|
||||
};
|
||||
|
||||
const language = i18n.resolvedLanguage || i18n.language || "en";
|
||||
|
||||
@@ -15,6 +15,7 @@ export const settingsSchema = z.object({
|
||||
skipClaudeOnboarding: z.boolean().optional(),
|
||||
launchOnStartup: z.boolean().optional(),
|
||||
enableLocalProxy: z.boolean().optional(),
|
||||
usageDashboardRefreshIntervalMs: z.number().optional(),
|
||||
preserveCodexOfficialAuthOnSwitch: z.boolean().optional(),
|
||||
unifyCodexSessionHistory: z.boolean().optional(),
|
||||
language: z.enum(["en", "zh", "zh-TW", "ja"]).optional(),
|
||||
|
||||
@@ -361,6 +361,7 @@ export interface Settings {
|
||||
proxyConfirmed?: boolean;
|
||||
// User has confirmed the usage query first-run notice
|
||||
usageConfirmed?: boolean;
|
||||
usageDashboardRefreshIntervalMs?: number;
|
||||
// User has confirmed the stream check first-run notice
|
||||
streamCheckConfirmed?: boolean;
|
||||
// Whether to show the failover toggle independently on the main page
|
||||
|
||||
Reference in New Issue
Block a user