Files
CC-Switch/src/lib/query/queries.ts
T
TinsFox f0e8ba1d8f feat: session manger (#867)
* feat: init session manger

* feat: persist selected app to localStorage

- Save app selection when switching providers
- Restore last selected app on page load

* feat: persist current view to localStorage

- Save view selection when switching tabs
- Restore last selected view on page load

* styles: update ui

* feat: Improve macOS Terminal activation and refactor Kitty launch to use  command with user's default shell.

* fix: session view

* feat: toc

* feat: Implement FlexSearch for improved session search functionality.

* feat: Redesign session manager search and filter UI for a more compact and dynamic experience.

* refactor: modularize session manager by extracting components and utility functions into dedicated files.

* feat: Enhance session terminal launching with support for iTerm2, Ghostty, WezTerm, and Alacritty, including UI and custom configuration options.

* feat: Conditionally render terminal selection and resume session buttons only on macOS.
2026-02-02 11:12:30 +08:00

167 lines
4.5 KiB
TypeScript

import {
useQuery,
type UseQueryResult,
keepPreviousData,
} from "@tanstack/react-query";
import {
providersApi,
settingsApi,
usageApi,
sessionsApi,
type AppId,
} from "@/lib/api";
import type {
Provider,
Settings,
UsageResult,
SessionMeta,
SessionMessage,
} from "@/types";
const sortProviders = (
providers: Record<string, Provider>,
): Record<string, Provider> => {
const sortedEntries = Object.values(providers)
.sort((a, b) => {
const indexA = a.sortIndex ?? Number.MAX_SAFE_INTEGER;
const indexB = b.sortIndex ?? Number.MAX_SAFE_INTEGER;
if (indexA !== indexB) {
return indexA - indexB;
}
const timeA = a.createdAt ?? 0;
const timeB = b.createdAt ?? 0;
if (timeA === timeB) {
return a.name.localeCompare(b.name, "zh-CN");
}
return timeA - timeB;
})
.map((provider) => [provider.id, provider] as const);
return Object.fromEntries(sortedEntries);
};
export interface ProvidersQueryData {
providers: Record<string, Provider>;
currentProviderId: string;
}
export interface UseProvidersQueryOptions {
isProxyRunning?: boolean; // 代理服务是否运行中
}
export const useProvidersQuery = (
appId: AppId,
options?: UseProvidersQueryOptions,
): UseQueryResult<ProvidersQueryData> => {
const { isProxyRunning = false } = options || {};
return useQuery({
queryKey: ["providers", appId],
placeholderData: keepPreviousData,
// 当代理服务运行时,每 10 秒刷新一次供应商列表
// 这样可以自动反映后端熔断器自动禁用代理目标的变更
refetchInterval: isProxyRunning ? 10000 : false,
queryFn: async () => {
let providers: Record<string, Provider> = {};
let currentProviderId = "";
try {
providers = await providersApi.getAll(appId);
} catch (error) {
console.error("获取供应商列表失败:", error);
}
try {
currentProviderId = await providersApi.getCurrent(appId);
} catch (error) {
console.error("获取当前供应商失败:", error);
}
if (Object.keys(providers).length === 0) {
try {
const success = await providersApi.importDefault(appId);
if (success) {
providers = await providersApi.getAll(appId);
currentProviderId = await providersApi.getCurrent(appId);
}
} catch (error) {
console.error("导入默认配置失败:", error);
}
}
return {
providers: sortProviders(providers),
currentProviderId,
};
},
});
};
export const useSettingsQuery = (): UseQueryResult<Settings> => {
return useQuery({
queryKey: ["settings"],
queryFn: async () => settingsApi.get(),
});
};
export interface UseUsageQueryOptions {
enabled?: boolean;
autoQueryInterval?: number; // 自动查询间隔(分钟),0 表示禁用
}
export const useUsageQuery = (
providerId: string,
appId: AppId,
options?: UseUsageQueryOptions,
) => {
const { enabled = true, autoQueryInterval = 0 } = options || {};
// 计算 staleTime:如果有自动刷新间隔,使用该间隔;否则默认 5 分钟
// 这样可以避免切换 app 页面时重复触发查询
const staleTime =
autoQueryInterval > 0
? autoQueryInterval * 60 * 1000 // 与刷新间隔保持一致
: 5 * 60 * 1000; // 默认 5 分钟
const query = useQuery<UsageResult>({
queryKey: ["usage", providerId, appId],
queryFn: async () => usageApi.query(providerId, appId),
enabled: enabled && !!providerId,
refetchInterval:
autoQueryInterval > 0
? Math.max(autoQueryInterval, 1) * 60 * 1000 // 最小1分钟
: false,
refetchIntervalInBackground: true, // 后台也继续定时查询
refetchOnWindowFocus: false,
retry: false,
staleTime, // 使用动态计算的缓存时间
gcTime: 10 * 60 * 1000, // 缓存保留 10 分钟(组件卸载后)
});
return {
...query,
lastQueriedAt: query.dataUpdatedAt || null,
};
};
export const useSessionsQuery = () => {
return useQuery<SessionMeta[]>({
queryKey: ["sessions"],
queryFn: async () => sessionsApi.list(),
staleTime: 30 * 1000,
});
};
export const useSessionMessagesQuery = (
providerId?: string,
sourcePath?: string,
) => {
return useQuery<SessionMessage[]>({
queryKey: ["sessionMessages", providerId, sourcePath],
queryFn: async () => sessionsApi.getMessages(providerId!, sourcePath!),
enabled: Boolean(providerId && sourcePath),
staleTime: 30 * 1000,
});
};