mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-08-04 19:45:34 +08:00
fix(import): refresh all providers immediately after SQL import
- Remove setTimeout delay that could be cancelled on component unmount - Invalidate all providers cache (not just current app) since import affects all apps - Call onImportSuccess before sync to ensure UI refresh even if sync fails - Update i18n: "Data refreshed" (past tense, reflecting immediate action)
This commit is contained in:
+16
-1
@@ -2,6 +2,7 @@ import { useEffect, useMemo, useState, useRef } from "react";
|
|||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import { invoke } from "@tauri-apps/api/core";
|
import { invoke } from "@tauri-apps/api/core";
|
||||||
|
import { useQueryClient } from "@tanstack/react-query";
|
||||||
import {
|
import {
|
||||||
Plus,
|
Plus,
|
||||||
Settings,
|
Settings,
|
||||||
@@ -45,6 +46,7 @@ type View = "providers" | "settings" | "prompts" | "skills" | "mcp" | "agents";
|
|||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
const [activeApp, setActiveApp] = useState<AppId>("claude");
|
const [activeApp, setActiveApp] = useState<AppId>("claude");
|
||||||
const [currentView, setCurrentView] = useState<View>("providers");
|
const [currentView, setCurrentView] = useState<View>("providers");
|
||||||
@@ -258,7 +260,20 @@ function App() {
|
|||||||
|
|
||||||
// 导入配置成功后刷新
|
// 导入配置成功后刷新
|
||||||
const handleImportSuccess = async () => {
|
const handleImportSuccess = async () => {
|
||||||
await refetch();
|
try {
|
||||||
|
// 导入会影响所有应用的供应商数据:刷新所有 providers 缓存
|
||||||
|
await queryClient.invalidateQueries({
|
||||||
|
queryKey: ["providers"],
|
||||||
|
refetchType: "all",
|
||||||
|
});
|
||||||
|
await queryClient.refetchQueries({
|
||||||
|
queryKey: ["providers"],
|
||||||
|
type: "all",
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error("[App] Failed to refresh providers after import", error);
|
||||||
|
await refetch();
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
await providersApi.updateTrayMenu();
|
await providersApi.updateTrayMenu();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useCallback, useEffect, useRef, useState } from "react";
|
import { useCallback, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import { settingsApi } from "@/lib/api";
|
import { settingsApi } from "@/lib/api";
|
||||||
@@ -39,15 +39,6 @@ export function useImportExport(
|
|||||||
const [errorMessage, setErrorMessage] = useState<string | null>(null);
|
const [errorMessage, setErrorMessage] = useState<string | null>(null);
|
||||||
const [backupId, setBackupId] = useState<string | null>(null);
|
const [backupId, setBackupId] = useState<string | null>(null);
|
||||||
const [isImporting, setIsImporting] = useState(false);
|
const [isImporting, setIsImporting] = useState(false);
|
||||||
const successTimerRef = useRef<number | null>(null);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
return () => {
|
|
||||||
if (successTimerRef.current) {
|
|
||||||
window.clearTimeout(successTimerRef.current);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const clearSelection = useCallback(() => {
|
const clearSelection = useCallback(() => {
|
||||||
setSelectedFile("");
|
setSelectedFile("");
|
||||||
@@ -105,6 +96,10 @@ export function useImportExport(
|
|||||||
}
|
}
|
||||||
|
|
||||||
setBackupId(result.backupId ?? null);
|
setBackupId(result.backupId ?? null);
|
||||||
|
// 导入成功后立即触发外部刷新(与 live 同步结果解耦)
|
||||||
|
// - 避免 sync 失败时 UI 不刷新
|
||||||
|
// - 避免依赖 setTimeout(组件卸载会取消)
|
||||||
|
void onImportSuccess?.();
|
||||||
|
|
||||||
const syncResult = await syncCurrentProvidersLiveSafe();
|
const syncResult = await syncCurrentProvidersLiveSafe();
|
||||||
if (syncResult.ok) {
|
if (syncResult.ok) {
|
||||||
@@ -114,10 +109,6 @@ export function useImportExport(
|
|||||||
defaultValue: "配置导入成功",
|
defaultValue: "配置导入成功",
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
successTimerRef.current = window.setTimeout(() => {
|
|
||||||
void onImportSuccess?.();
|
|
||||||
}, 1500);
|
|
||||||
} else {
|
} else {
|
||||||
console.error(
|
console.error(
|
||||||
"[useImportExport] Failed to sync live config",
|
"[useImportExport] Failed to sync live config",
|
||||||
|
|||||||
@@ -162,7 +162,7 @@
|
|||||||
"selectFileFailed": "Please choose a valid SQL backup file",
|
"selectFileFailed": "Please choose a valid SQL backup file",
|
||||||
"configCorrupted": "SQL file may be corrupted or invalid",
|
"configCorrupted": "SQL file may be corrupted or invalid",
|
||||||
"backupId": "Backup ID",
|
"backupId": "Backup ID",
|
||||||
"autoReload": "Data will refresh automatically in 2 seconds...",
|
"autoReload": "Data refreshed",
|
||||||
"languageOptionChinese": "中文",
|
"languageOptionChinese": "中文",
|
||||||
"languageOptionEnglish": "English",
|
"languageOptionEnglish": "English",
|
||||||
"languageOptionJapanese": "日本語",
|
"languageOptionJapanese": "日本語",
|
||||||
|
|||||||
@@ -162,7 +162,7 @@
|
|||||||
"selectFileFailed": "有効な SQL バックアップファイルを選択してください",
|
"selectFileFailed": "有効な SQL バックアップファイルを選択してください",
|
||||||
"configCorrupted": "SQL ファイルが壊れているか形式が無効な可能性があります",
|
"configCorrupted": "SQL ファイルが壊れているか形式が無効な可能性があります",
|
||||||
"backupId": "バックアップ ID",
|
"backupId": "バックアップ ID",
|
||||||
"autoReload": "2 秒後に自動で再読み込みします...",
|
"autoReload": "データを更新しました",
|
||||||
"languageOptionChinese": "中文",
|
"languageOptionChinese": "中文",
|
||||||
"languageOptionEnglish": "English",
|
"languageOptionEnglish": "English",
|
||||||
"languageOptionJapanese": "日本語",
|
"languageOptionJapanese": "日本語",
|
||||||
|
|||||||
@@ -162,7 +162,7 @@
|
|||||||
"selectFileFailed": "请选择有效的 SQL 备份文件",
|
"selectFileFailed": "请选择有效的 SQL 备份文件",
|
||||||
"configCorrupted": "SQL 文件可能已损坏或格式不正确",
|
"configCorrupted": "SQL 文件可能已损坏或格式不正确",
|
||||||
"backupId": "备份ID",
|
"backupId": "备份ID",
|
||||||
"autoReload": "数据将在2秒后自动刷新...",
|
"autoReload": "数据已刷新",
|
||||||
"languageOptionChinese": "中文",
|
"languageOptionChinese": "中文",
|
||||||
"languageOptionEnglish": "English",
|
"languageOptionEnglish": "English",
|
||||||
"languageOptionJapanese": "日本語",
|
"languageOptionJapanese": "日本語",
|
||||||
|
|||||||
@@ -110,12 +110,6 @@ describe("useImportExport Hook", () => {
|
|||||||
expect(result.current.status).toBe("success");
|
expect(result.current.status).toBe("success");
|
||||||
expect(result.current.backupId).toBe("backup-123");
|
expect(result.current.backupId).toBe("backup-123");
|
||||||
expect(toastSuccessMock).toHaveBeenCalledTimes(1);
|
expect(toastSuccessMock).toHaveBeenCalledTimes(1);
|
||||||
|
|
||||||
// Skip delay to execute callback
|
|
||||||
await act(async () => {
|
|
||||||
vi.runOnlyPendingTimers();
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(onImportSuccess).toHaveBeenCalledTimes(1);
|
expect(onImportSuccess).toHaveBeenCalledTimes(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user