fix(auth-files): save pending custom excluded rule

This commit is contained in:
Supra4E8C
2026-07-11 03:22:26 +08:00
parent 2201fe1ce7
commit 73c3b15a16
3 changed files with 35 additions and 14 deletions
@@ -15,6 +15,13 @@ export function normalizeOAuthExcludedRules(values: Iterable<string>): string[]
return rules;
}
export function getEffectiveOAuthExcludedRules(
selectedRules: Iterable<string>,
customRule: string
): string[] {
return normalizeOAuthExcludedRules([...selectedRules, customRule]);
}
export function hasOAuthExcludedRule(values: Iterable<string>, candidate: string): boolean {
const candidateKey = getRuleKey(candidate);
if (!candidateKey) return false;
+15 -14
View File
@@ -21,6 +21,7 @@ import {
import { getStringSetSignature, isOAuthEditorDirty } from '@/features/authFiles/oauthEditorState';
import {
getCustomOAuthExcludedRules,
getEffectiveOAuthExcludedRules,
hasOAuthExcludedRule,
normalizeOAuthExcludedRules,
updateOAuthExcludedRule,
@@ -87,11 +88,15 @@ export function AuthFilesOAuthExcludedEditPage() {
() => getStringSetSignature(normalizeOAuthExcludedRules(excluded[resolvedProviderKey] ?? [])),
[excluded, resolvedProviderKey]
);
const selectedModelsSignature = useMemo(
() => getStringSetSignature(normalizeOAuthExcludedRules(selectedModels)),
[selectedModels]
const effectiveRules = useMemo(
() => getEffectiveOAuthExcludedRules(selectedModels, customRule),
[customRule, selectedModels]
);
const contentDirty = baselineModelsSignature !== selectedModelsSignature;
const effectiveRulesSignature = useMemo(
() => getStringSetSignature(effectiveRules),
[effectiveRules]
);
const contentDirty = baselineModelsSignature !== effectiveRulesSignature;
const customRules = useMemo(
() =>
getCustomOAuthExcludedRules(
@@ -104,7 +109,7 @@ export function AuthFilesOAuthExcludedEditPage() {
initialProviderKey,
provider,
baselineModelsSignature,
selectedModelsSignature
effectiveRulesSignature
);
const unsavedChangesDialog = useMemo(
() => ({
@@ -298,14 +303,10 @@ export function AuthFilesOAuthExcludedEditPage() {
}, []);
const handleAddCustomRule = useCallback(() => {
const nextRule = customRule.trim();
if (!nextRule) return;
setSelectedModels((prev) => {
if (hasOAuthExcludedRule(prev, nextRule)) return prev;
return new Set(updateOAuthExcludedRule(prev, nextRule, true));
});
if (!customRule.trim()) return;
setSelectedModels(new Set(effectiveRules));
setCustomRule('');
}, [customRule]);
}, [customRule, effectiveRules]);
const handleSave = useCallback(async () => {
const normalizedProvider = normalizeProviderKey(provider);
@@ -314,7 +315,7 @@ export function AuthFilesOAuthExcludedEditPage() {
return;
}
const models = normalizeOAuthExcludedRules(selectedModels);
const models = effectiveRules;
setSaving(true);
try {
if (models.length) {
@@ -331,7 +332,7 @@ export function AuthFilesOAuthExcludedEditPage() {
} finally {
setSaving(false);
}
}, [allowNextNavigation, handleBack, isEditing, provider, selectedModels, showNotification, t]);
}, [allowNextNavigation, effectiveRules, handleBack, isEditing, provider, showNotification, t]);
const canSave = !disableControls && !saving && !excludedUnsupported;
+13
View File
@@ -1,6 +1,7 @@
import { describe, expect, test } from 'bun:test';
import {
getCustomOAuthExcludedRules,
getEffectiveOAuthExcludedRules,
hasOAuthExcludedRule,
normalizeOAuthExcludedRules,
updateOAuthExcludedRule,
@@ -28,4 +29,16 @@ describe('OAuth excluded rules', () => {
)
).toEqual(['gpt-*', 'retired-model']);
});
test('includes a pending custom rule in the effective rules', () => {
expect(getEffectiveOAuthExcludedRules(['gpt-4o'], ' gpt-* ')).toEqual(['gpt-4o', 'gpt-*']);
});
test('ignores a blank pending custom rule', () => {
expect(getEffectiveOAuthExcludedRules(['gpt-4o'], ' ')).toEqual(['gpt-4o']);
});
test('keeps the original spelling for a case-insensitive duplicate', () => {
expect(getEffectiveOAuthExcludedRules(['GPT-4o'], 'gpt-4O')).toEqual(['GPT-4o']);
});
});