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 & { 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"] }); }, }); };