Files
CC-Switch/src/hooks/useSkills.helpers.ts
T
涂瑜 29e87487a1 fix(skills): prevent duplicate imports when import button is double-clicked (#2211)
Closes #2139

Two related defects let the installed-skills count balloon when users
tap the import confirm button multiple times — either deliberately or
because the button is still clickable while a slow import is in flight:

- The confirm button only disabled itself while `selected.size === 0`,
  so it stayed clickable during a pending mutation. Each extra click
  triggered another `importFromApps` mutation.
- `useImportSkillsFromApps` appended the server response to the
  installed cache without deduping, so re-firing the mutation stacked
  the same skills into the list again.

Disable the confirm (and cancel) buttons while the mutation is pending
— matching the `isRestoring` / `isDeleting` pattern already used by
`RestoreSkillsDialog` — and merge success payloads by
`InstalledSkill.id` so repeated results overwrite rather than
accumulate.

The merge is extracted as a pure `mergeImportedSkills` reducer to make
the behaviour unit-testable and to short-circuit on an empty payload,
returning the existing reference so React Query does not notify
subscribers about a no-op cache update.
2026-04-23 10:25:01 +08:00

20 lines
733 B
TypeScript

import type { InstalledSkill } from "@/lib/api/skills";
/**
* 合并本次导入结果到已安装缓存,按 id 去重。
*
* 同一技能重复出现时以新记录为准,避免 mutation 被重复触发时
* 在 installed 列表中看到重复条目。imported 为空时返回原引用,
* 让 React Query 跳过无谓的订阅者通知。
*/
export function mergeImportedSkills(
existing: InstalledSkill[] | undefined,
imported: InstalledSkill[],
): InstalledSkill[] {
if (!existing) return imported;
if (imported.length === 0) return existing;
const importedIds = new Set(imported.map((s) => s.id));
const preserved = existing.filter((s) => !importedIds.has(s.id));
return [...preserved, ...imported];
}