Files
cc-switch/src/lib/query/mutations.ts
T
Jason 57713dd336 feat(claude): show proxy hint when switching to OpenAI Chat format provider
Display an info toast when switching to a provider that uses OpenAI Chat
format (apiFormat === "openai_chat"), reminding users to enable the proxy
service. Move toast logic from mutation to useProviderActions for better
control over notification content based on provider properties.
2026-01-30 16:40:41 +08:00

213 lines
5.9 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) => {
toast.error(
t("notifications.addFailed", {
defaultValue: "添加供应商失败: {{error}}",
error: error.message,
}),
);
},
});
};
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) => {
toast.error(
t("notifications.updateFailed", {
defaultValue: "更新供应商失败: {{error}}",
error: error.message,
}),
);
},
});
};
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) => {
toast.error(
t("notifications.deleteFailed", {
defaultValue: "删除供应商失败: {{error}}",
error: error.message,
}),
);
},
});
};
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"] });
},
});
};