mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-07-24 12:44:18 +08:00
feat(skills): 优化技能安装/卸载的缓存更新策略 (#1573)
- 修改安装、卸载、导入、ZIP安装等操作的缓存更新逻辑,从invalidateQueries改为直接setQueryData - 为已安装和可发现技能查询添加keepPreviousData和staleTime: Infinity配置 - 修复会话管理页面布局滚动问题,添加min-h-0防止内容溢出
This commit is contained in:
@@ -208,12 +208,15 @@ export function SessionManagerPage({ appId }: { appId: string }) {
|
||||
|
||||
return (
|
||||
<TooltipProvider>
|
||||
<div className="mx-auto px-4 sm:px-6 flex flex-col flex-1 min-h-0">
|
||||
<div
|
||||
className="mx-auto px-4 sm:px-6 flex flex-col h-full min-h-0"
|
||||
onWheel={(e) => e.stopPropagation()}
|
||||
>
|
||||
<div className="flex-1 overflow-hidden flex flex-col gap-4">
|
||||
{/* 主内容区域 - 左右分栏 */}
|
||||
<div className="flex-1 overflow-hidden grid gap-4 md:grid-cols-[320px_1fr]">
|
||||
{/* 左侧会话列表 */}
|
||||
<Card className="flex flex-col overflow-hidden">
|
||||
<Card className="flex flex-col flex-1 min-h-0 overflow-hidden">
|
||||
<CardHeader className="py-2 px-3 border-b">
|
||||
{isSearchOpen ? (
|
||||
<div className="relative flex-1">
|
||||
@@ -387,7 +390,7 @@ export function SessionManagerPage({ appId }: { appId: string }) {
|
||||
</div>
|
||||
)}
|
||||
</CardHeader>
|
||||
<CardContent className="flex-1 overflow-hidden p-0">
|
||||
<CardContent className="flex-1 min-h-0 p-0">
|
||||
<ScrollArea className="h-full">
|
||||
<div className="p-2">
|
||||
{isLoading ? (
|
||||
@@ -605,7 +608,7 @@ export function SessionManagerPage({ appId }: { appId: string }) {
|
||||
</CardHeader>
|
||||
|
||||
{/* 消息列表区域 */}
|
||||
<CardContent className="flex-1 overflow-hidden p-0">
|
||||
<CardContent className="flex-1 min-h-0 p-0">
|
||||
<div className="flex h-full min-w-0">
|
||||
{/* 消息列表 */}
|
||||
<ScrollArea className="flex-1 min-w-0">
|
||||
|
||||
@@ -110,7 +110,16 @@ const UnifiedSkillsPanel = React.forwardRef<
|
||||
message: t("skills.uninstallConfirm", { name: skill.name }),
|
||||
onConfirm: async () => {
|
||||
try {
|
||||
const result = await uninstallMutation.mutateAsync(skill.id);
|
||||
// 构建 skillKey 用于更新 discoverable 缓存
|
||||
const installName =
|
||||
skill.directory.split(/[/\\]/).pop()?.toLowerCase() ||
|
||||
skill.directory.toLowerCase();
|
||||
const skillKey = `${installName}:${skill.repoOwner?.toLowerCase() || ""}:${skill.repoName?.toLowerCase() || ""}`;
|
||||
|
||||
const result = await uninstallMutation.mutateAsync({
|
||||
id: skill.id,
|
||||
skillKey,
|
||||
});
|
||||
setConfirmDialog(null);
|
||||
toast.success(t("skills.uninstallSuccess", { name: skill.name }), {
|
||||
description: result.backupPath
|
||||
|
||||
+91
-14
@@ -1,4 +1,4 @@
|
||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import { useMutation, useQuery, useQueryClient, keepPreviousData } from "@tanstack/react-query";
|
||||
import {
|
||||
skillsApi,
|
||||
type SkillBackupEntry,
|
||||
@@ -10,11 +10,15 @@ import type { AppId } from "@/lib/api/types";
|
||||
|
||||
/**
|
||||
* 查询所有已安装的 Skills
|
||||
* 使用 staleTime: Infinity 和 placeholderData: keepPreviousData
|
||||
* 实现首次进入使用缓存,只有刷新时才重新获取
|
||||
*/
|
||||
export function useInstalledSkills() {
|
||||
return useQuery({
|
||||
queryKey: ["skills", "installed"],
|
||||
queryFn: () => skillsApi.getInstalled(),
|
||||
staleTime: Infinity,
|
||||
placeholderData: keepPreviousData,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -38,17 +42,21 @@ export function useDeleteSkillBackup() {
|
||||
|
||||
/**
|
||||
* 发现可安装的 Skills(从仓库获取)
|
||||
* 使用 staleTime: Infinity 和 placeholderData: keepPreviousData
|
||||
* 实现首次进入使用缓存,只有刷新时才重新获取
|
||||
*/
|
||||
export function useDiscoverableSkills() {
|
||||
return useQuery({
|
||||
queryKey: ["skills", "discoverable"],
|
||||
queryFn: () => skillsApi.discoverAvailable(),
|
||||
staleTime: Infinity, // 无限缓存,直到仓库变化时 invalidate
|
||||
staleTime: Infinity,
|
||||
placeholderData: keepPreviousData,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 安装 Skill
|
||||
* 成功后直接更新缓存,不触发重新加载/刷新
|
||||
*/
|
||||
export function useInstallSkill() {
|
||||
const queryClient = useQueryClient();
|
||||
@@ -60,23 +68,76 @@ export function useInstallSkill() {
|
||||
skill: DiscoverableSkill;
|
||||
currentApp: AppId;
|
||||
}) => skillsApi.installUnified(skill, currentApp),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ["skills", "installed"] });
|
||||
queryClient.invalidateQueries({ queryKey: ["skills", "discoverable"] });
|
||||
onSuccess: (installedSkill, _vars, _ctx) => {
|
||||
const { skill } = _vars;
|
||||
// 直接更新 installed 缓存
|
||||
queryClient.setQueryData<InstalledSkill[]>(
|
||||
["skills", "installed"],
|
||||
(oldData) => {
|
||||
if (!oldData) return [installedSkill];
|
||||
return [...oldData, installedSkill];
|
||||
},
|
||||
);
|
||||
|
||||
// 更新 discoverable 缓存中对应技能的 installed 状态
|
||||
const installName =
|
||||
skill.directory.split(/[/\\]/).pop()?.toLowerCase() ||
|
||||
skill.directory.toLowerCase();
|
||||
const skillKey = `${installName}:${skill.repoOwner.toLowerCase()}:${skill.repoName.toLowerCase()}`;
|
||||
|
||||
queryClient.setQueryData<DiscoverableSkill[]>(
|
||||
["skills", "discoverable"],
|
||||
(oldData) => {
|
||||
if (!oldData) return oldData;
|
||||
return oldData.map((s) => {
|
||||
if (s.key === skillKey) {
|
||||
return { ...s, installed: true };
|
||||
}
|
||||
return s;
|
||||
});
|
||||
},
|
||||
);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 卸载 Skill
|
||||
* 成功后直接更新缓存,不触发重新加载/刷新
|
||||
*/
|
||||
export function useUninstallSkill() {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (id: string) => skillsApi.uninstallUnified(id),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ["skills", "installed"] });
|
||||
queryClient.invalidateQueries({ queryKey: ["skills", "discoverable"] });
|
||||
mutationFn: ({
|
||||
id,
|
||||
skillKey,
|
||||
}: {
|
||||
id: string;
|
||||
skillKey: string;
|
||||
}) => skillsApi.uninstallUnified(id).then((result) => ({ ...result, skillKey })),
|
||||
onSuccess: ({ skillKey }, _vars) => {
|
||||
// 直接更新 installed 缓存,移除该 skill
|
||||
queryClient.setQueryData<InstalledSkill[]>(
|
||||
["skills", "installed"],
|
||||
(oldData) => {
|
||||
if (!oldData) return oldData;
|
||||
return oldData.filter((s) => s.id !== _vars.id);
|
||||
},
|
||||
);
|
||||
|
||||
// 更新 discoverable 缓存中对应技能的 installed 状态
|
||||
queryClient.setQueryData<DiscoverableSkill[]>(
|
||||
["skills", "discoverable"],
|
||||
(oldData) => {
|
||||
if (!oldData) return oldData;
|
||||
return oldData.map((s) => {
|
||||
if (s.key === skillKey) {
|
||||
return { ...s, installed: false };
|
||||
}
|
||||
return s;
|
||||
});
|
||||
},
|
||||
);
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -132,14 +193,23 @@ export function useScanUnmanagedSkills() {
|
||||
|
||||
/**
|
||||
* 从应用目录导入 Skills
|
||||
* 成功后直接更新缓存,不触发重新加载/刷新
|
||||
*/
|
||||
export function useImportSkillsFromApps() {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (imports: ImportSkillSelection[]) =>
|
||||
skillsApi.importFromApps(imports),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ["skills", "installed"] });
|
||||
onSuccess: (importedSkills) => {
|
||||
// 直接更新 installed 缓存
|
||||
queryClient.setQueryData<InstalledSkill[]>(
|
||||
["skills", "installed"],
|
||||
(oldData) => {
|
||||
if (!oldData) return importedSkills;
|
||||
return [...oldData, ...importedSkills];
|
||||
},
|
||||
);
|
||||
// 刷新 unmanaged 列表(已被导入的应该移除)
|
||||
queryClient.invalidateQueries({ queryKey: ["skills", "unmanaged"] });
|
||||
},
|
||||
});
|
||||
@@ -186,6 +256,7 @@ export function useRemoveSkillRepo() {
|
||||
|
||||
/**
|
||||
* 从 ZIP 文件安装 Skills
|
||||
* 成功后直接更新缓存,不触发重新加载/刷新
|
||||
*/
|
||||
export function useInstallSkillsFromZip() {
|
||||
const queryClient = useQueryClient();
|
||||
@@ -197,9 +268,15 @@ export function useInstallSkillsFromZip() {
|
||||
filePath: string;
|
||||
currentApp: AppId;
|
||||
}) => skillsApi.installFromZip(filePath, currentApp),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ["skills", "installed"] });
|
||||
queryClient.invalidateQueries({ queryKey: ["skills", "unmanaged"] });
|
||||
onSuccess: (installedSkills) => {
|
||||
// 直接更新 installed 缓存
|
||||
queryClient.setQueryData<InstalledSkill[]>(
|
||||
["skills", "installed"],
|
||||
(oldData) => {
|
||||
if (!oldData) return installedSkills;
|
||||
return [...oldData, ...installedSkills];
|
||||
},
|
||||
);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user