mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-07-26 06:24:32 +08:00
faa82a5b86
Provider add/update/delete mutations were using error.message directly, which doesn't extract Tauri invoke errors properly. Now using the same extractErrorMessage pattern as the rest of the codebase.
216 lines
6.1 KiB
TypeScript
216 lines
6.1 KiB
TypeScript
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
|
import { useTranslation } from "react-i18next";
|
|
import { toast } from "sonner";
|
|
import { providersApi, settingsApi, type AppId } from "@/lib/api";
|
|
import type { Provider, Settings } from "@/types";
|
|
import { extractErrorMessage } from "@/utils/errorUtils";
|
|
import { generateUUID } from "@/utils/uuid";
|
|
|
|
export const useAddProviderMutation = (appId: AppId) => {
|
|
const queryClient = useQueryClient();
|
|
const { t } = useTranslation();
|
|
|
|
return useMutation({
|
|
mutationFn: async (
|
|
providerInput: Omit<Provider, "id"> & { providerKey?: string },
|
|
) => {
|
|
let id: string;
|
|
|
|
if (appId === "opencode") {
|
|
// OpenCode: use user-provided providerKey as ID
|
|
if (!providerInput.providerKey) {
|
|
throw new Error("Provider key is required for OpenCode");
|
|
}
|
|
id = providerInput.providerKey;
|
|
} else {
|
|
// Other apps: use random UUID
|
|
id = generateUUID();
|
|
}
|
|
|
|
const newProvider: Provider = {
|
|
...providerInput,
|
|
id,
|
|
createdAt: Date.now(),
|
|
};
|
|
// Remove providerKey from the provider object before saving
|
|
delete (newProvider as any).providerKey;
|
|
|
|
await providersApi.add(newProvider, appId);
|
|
return newProvider;
|
|
},
|
|
onSuccess: async () => {
|
|
await queryClient.invalidateQueries({ queryKey: ["providers", appId] });
|
|
|
|
// 更新托盘菜单(失败不影响主操作)
|
|
try {
|
|
await providersApi.updateTrayMenu();
|
|
} catch (trayError) {
|
|
console.error(
|
|
"Failed to update tray menu after adding provider",
|
|
trayError,
|
|
);
|
|
}
|
|
|
|
toast.success(
|
|
t("notifications.providerAdded", {
|
|
defaultValue: "供应商已添加",
|
|
}),
|
|
{
|
|
closeButton: true,
|
|
},
|
|
);
|
|
},
|
|
onError: (error: Error) => {
|
|
const detail = extractErrorMessage(error) || t("common.unknown");
|
|
toast.error(
|
|
t("notifications.addFailed", {
|
|
defaultValue: "添加供应商失败: {{error}}",
|
|
error: detail,
|
|
}),
|
|
);
|
|
},
|
|
});
|
|
};
|
|
|
|
export const useUpdateProviderMutation = (appId: AppId) => {
|
|
const queryClient = useQueryClient();
|
|
const { t } = useTranslation();
|
|
|
|
return useMutation({
|
|
mutationFn: async (provider: Provider) => {
|
|
await providersApi.update(provider, appId);
|
|
return provider;
|
|
},
|
|
onSuccess: async () => {
|
|
await queryClient.invalidateQueries({ queryKey: ["providers", appId] });
|
|
toast.success(
|
|
t("notifications.updateSuccess", {
|
|
defaultValue: "供应商更新成功",
|
|
}),
|
|
{
|
|
closeButton: true,
|
|
},
|
|
);
|
|
},
|
|
onError: (error: Error) => {
|
|
const detail = extractErrorMessage(error) || t("common.unknown");
|
|
toast.error(
|
|
t("notifications.updateFailed", {
|
|
defaultValue: "更新供应商失败: {{error}}",
|
|
error: detail,
|
|
}),
|
|
);
|
|
},
|
|
});
|
|
};
|
|
|
|
export const useDeleteProviderMutation = (appId: AppId) => {
|
|
const queryClient = useQueryClient();
|
|
const { t } = useTranslation();
|
|
|
|
return useMutation({
|
|
mutationFn: async (providerId: string) => {
|
|
await providersApi.delete(providerId, appId);
|
|
},
|
|
onSuccess: async () => {
|
|
await queryClient.invalidateQueries({ queryKey: ["providers", appId] });
|
|
|
|
// 更新托盘菜单(失败不影响主操作)
|
|
try {
|
|
await providersApi.updateTrayMenu();
|
|
} catch (trayError) {
|
|
console.error(
|
|
"Failed to update tray menu after deleting provider",
|
|
trayError,
|
|
);
|
|
}
|
|
|
|
toast.success(
|
|
t("notifications.deleteSuccess", {
|
|
defaultValue: "供应商已删除",
|
|
}),
|
|
{
|
|
closeButton: true,
|
|
},
|
|
);
|
|
},
|
|
onError: (error: Error) => {
|
|
const detail = extractErrorMessage(error) || t("common.unknown");
|
|
toast.error(
|
|
t("notifications.deleteFailed", {
|
|
defaultValue: "删除供应商失败: {{error}}",
|
|
error: detail,
|
|
}),
|
|
);
|
|
},
|
|
});
|
|
};
|
|
|
|
export const useSwitchProviderMutation = (appId: AppId) => {
|
|
const queryClient = useQueryClient();
|
|
const { t } = useTranslation();
|
|
|
|
return useMutation({
|
|
mutationFn: async (providerId: string) => {
|
|
return await providersApi.switch(providerId, appId);
|
|
},
|
|
onSuccess: async () => {
|
|
await queryClient.invalidateQueries({ queryKey: ["providers", appId] });
|
|
|
|
// OpenCode: also invalidate live provider IDs cache to update button state
|
|
if (appId === "opencode") {
|
|
await queryClient.invalidateQueries({
|
|
queryKey: ["opencodeLiveProviderIds"],
|
|
});
|
|
}
|
|
|
|
// 更新托盘菜单(失败不影响主操作)
|
|
try {
|
|
await providersApi.updateTrayMenu();
|
|
} catch (trayError) {
|
|
console.error(
|
|
"Failed to update tray menu after switching provider",
|
|
trayError,
|
|
);
|
|
}
|
|
|
|
// Note: Success toast is handled by useProviderActions.switchProvider
|
|
// to allow customization based on provider properties (e.g., apiFormat)
|
|
},
|
|
onError: (error: Error) => {
|
|
const detail = extractErrorMessage(error) || t("common.unknown");
|
|
|
|
// 标题与详情分离,便于扫描 + 一键复制
|
|
toast.error(
|
|
t("notifications.switchFailedTitle", { defaultValue: "切换失败" }),
|
|
{
|
|
description: t("notifications.switchFailed", {
|
|
defaultValue: "切换失败:{{error}}",
|
|
error: detail,
|
|
}),
|
|
duration: 6000,
|
|
action: {
|
|
label: t("common.copy", { defaultValue: "复制" }),
|
|
onClick: () => {
|
|
navigator.clipboard?.writeText(detail).catch(() => undefined);
|
|
},
|
|
},
|
|
},
|
|
);
|
|
},
|
|
});
|
|
};
|
|
|
|
export const useSaveSettingsMutation = () => {
|
|
const queryClient = useQueryClient();
|
|
|
|
return useMutation({
|
|
mutationFn: async (settings: Settings) => {
|
|
await settingsApi.save(settings);
|
|
},
|
|
onSuccess: async () => {
|
|
await queryClient.invalidateQueries({ queryKey: ["settings"] });
|
|
},
|
|
});
|
|
};
|