mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-05-25 07:20:41 +08:00
fix(provider-form): prevent duplicate submissions on rapid button clicks (#1352)
Make ProviderForm.handleSubmit async and await onSubmit so react-hook-form's isSubmitting state is tracked. Disable submit buttons in AddProviderDialog and EditProviderDialog while submission is in flight via onSubmittingChange callback.
This commit is contained in:
@@ -48,6 +48,7 @@ export function AddProviderDialog({
|
||||
const [universalFormOpen, setUniversalFormOpen] = useState(false);
|
||||
const [selectedUniversalPreset, setSelectedUniversalPreset] =
|
||||
useState<UniversalProviderPreset | null>(null);
|
||||
const [isFormSubmitting, setIsFormSubmitting] = useState(false);
|
||||
|
||||
const handleUniversalProviderSave = useCallback(
|
||||
async (provider: UniversalProvider) => {
|
||||
@@ -247,6 +248,7 @@ export function AddProviderDialog({
|
||||
<Button
|
||||
type="submit"
|
||||
form="provider-form"
|
||||
disabled={isFormSubmitting}
|
||||
className="bg-primary text-primary-foreground hover:bg-primary/90"
|
||||
>
|
||||
<Plus className="h-4 w-4 mr-2" />
|
||||
@@ -299,6 +301,7 @@ export function AddProviderDialog({
|
||||
submitLabel={t("common.add")}
|
||||
onSubmit={handleSubmit}
|
||||
onCancel={() => onOpenChange(false)}
|
||||
onSubmittingChange={setIsFormSubmitting}
|
||||
showButtons={false}
|
||||
/>
|
||||
</TabsContent>
|
||||
@@ -314,6 +317,7 @@ export function AddProviderDialog({
|
||||
submitLabel={t("common.add")}
|
||||
onSubmit={handleSubmit}
|
||||
onCancel={() => onOpenChange(false)}
|
||||
onSubmittingChange={setIsFormSubmitting}
|
||||
showButtons={false}
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -28,6 +28,7 @@ export function EditProviderDialog({
|
||||
isProxyTakeover = false,
|
||||
}: EditProviderDialogProps) {
|
||||
const { t } = useTranslation();
|
||||
const [isFormSubmitting, setIsFormSubmitting] = useState(false);
|
||||
|
||||
// 默认使用传入的 provider.settingsConfig,若当前编辑对象是"当前生效供应商",则尝试读取实时配置替换初始值
|
||||
const [liveSettings, setLiveSettings] = useState<Record<
|
||||
@@ -197,6 +198,7 @@ export function EditProviderDialog({
|
||||
<Button
|
||||
type="submit"
|
||||
form="provider-form"
|
||||
disabled={isFormSubmitting}
|
||||
className="bg-primary text-primary-foreground hover:bg-primary/90"
|
||||
>
|
||||
<Save className="h-4 w-4 mr-2" />
|
||||
@@ -210,6 +212,7 @@ export function EditProviderDialog({
|
||||
submitLabel={t("common.save")}
|
||||
onSubmit={handleSubmit}
|
||||
onCancel={() => onOpenChange(false)}
|
||||
onSubmittingChange={setIsFormSubmitting}
|
||||
initialData={initialData}
|
||||
showButtons={false}
|
||||
/>
|
||||
|
||||
@@ -104,10 +104,11 @@ interface ProviderFormProps {
|
||||
appId: AppId;
|
||||
providerId?: string;
|
||||
submitLabel: string;
|
||||
onSubmit: (values: ProviderFormValues) => void;
|
||||
onSubmit: (values: ProviderFormValues) => Promise<void> | void;
|
||||
onCancel: () => void;
|
||||
onUniversalPresetSelect?: (preset: UniversalProviderPreset) => void;
|
||||
onManageUniversalProviders?: () => void;
|
||||
onSubmittingChange?: (isSubmitting: boolean) => void;
|
||||
initialData?: {
|
||||
name?: string;
|
||||
websiteUrl?: string;
|
||||
@@ -129,6 +130,7 @@ export function ProviderForm({
|
||||
onCancel,
|
||||
onUniversalPresetSelect,
|
||||
onManageUniversalProviders,
|
||||
onSubmittingChange,
|
||||
initialData,
|
||||
showButtons = true,
|
||||
}: ProviderFormProps) {
|
||||
@@ -237,6 +239,7 @@ export function ProviderForm({
|
||||
defaultValues,
|
||||
mode: "onSubmit",
|
||||
});
|
||||
const { isSubmitting } = form.formState;
|
||||
|
||||
const handleSettingsConfigChange = useCallback(
|
||||
(config: string) => {
|
||||
@@ -257,6 +260,10 @@ export function ProviderForm({
|
||||
},
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
onSubmittingChange?.(isSubmitting);
|
||||
}, [isSubmitting, onSubmittingChange]);
|
||||
|
||||
const {
|
||||
apiKey,
|
||||
handleApiKeyChange,
|
||||
@@ -583,7 +590,7 @@ export function ProviderForm({
|
||||
|
||||
const [isCommonConfigModalOpen, setIsCommonConfigModalOpen] = useState(false);
|
||||
|
||||
const handleSubmit = (values: ProviderFormData) => {
|
||||
const handleSubmit = async (values: ProviderFormData) => {
|
||||
if (appId === "claude" && templateValueEntries.length > 0) {
|
||||
const validation = validateTemplateValues();
|
||||
if (!validation.isValid && validation.missingField) {
|
||||
@@ -892,7 +899,7 @@ export function ProviderForm({
|
||||
: undefined,
|
||||
};
|
||||
|
||||
onSubmit(payload);
|
||||
await onSubmit(payload);
|
||||
};
|
||||
|
||||
const groupedPresets = useMemo(() => {
|
||||
@@ -1600,7 +1607,7 @@ export function ProviderForm({
|
||||
<Button variant="outline" type="button" onClick={onCancel}>
|
||||
{t("common.cancel")}
|
||||
</Button>
|
||||
<Button type="submit">{submitLabel}</Button>
|
||||
<Button type="submit" disabled={isSubmitting}>{submitLabel}</Button>
|
||||
</div>
|
||||
)}
|
||||
</form>
|
||||
|
||||
Reference in New Issue
Block a user