mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-07-27 16:26:16 +08:00
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.
This commit is contained in:
@@ -454,6 +454,7 @@ const UnifiedSkillsPanel = React.forwardRef<
|
||||
{importDialogOpen && unmanagedSkills && (
|
||||
<ImportSkillsDialog
|
||||
skills={unmanagedSkills}
|
||||
isImporting={importMutation.isPending}
|
||||
onImport={handleImport}
|
||||
onClose={() => setImportDialogOpen(false)}
|
||||
/>
|
||||
@@ -600,6 +601,7 @@ interface ImportSkillsDialogProps {
|
||||
foundIn: string[];
|
||||
path: string;
|
||||
}>;
|
||||
isImporting: boolean;
|
||||
onImport: (imports: ImportSkillSelection[]) => void;
|
||||
onClose: () => void;
|
||||
}
|
||||
@@ -724,6 +726,7 @@ const RestoreSkillsDialog: React.FC<RestoreSkillsDialogProps> = ({
|
||||
|
||||
const ImportSkillsDialog: React.FC<ImportSkillsDialogProps> = ({
|
||||
skills,
|
||||
isImporting,
|
||||
onImport,
|
||||
onClose,
|
||||
}) => {
|
||||
@@ -846,10 +849,13 @@ const ImportSkillsDialog: React.FC<ImportSkillsDialogProps> = ({
|
||||
</div>
|
||||
|
||||
<div className="flex justify-end gap-3">
|
||||
<Button variant="outline" onClick={onClose}>
|
||||
<Button variant="outline" onClick={onClose} disabled={isImporting}>
|
||||
{t("common.cancel")}
|
||||
</Button>
|
||||
<Button onClick={handleImport} disabled={selected.size === 0}>
|
||||
<Button
|
||||
onClick={handleImport}
|
||||
disabled={selected.size === 0 || isImporting}
|
||||
>
|
||||
{t("skills.importSelected", { count: selected.size })}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user