import { useCallback, useEffect, useRef, useState } from "react"; import { useTranslation } from "react-i18next"; import { invoke } from "@tauri-apps/api/core"; import { listen } from "@tauri-apps/api/event"; import { exit } from "@tauri-apps/plugin-process"; import { Database, Download, RefreshCw, ExternalLink, FolderOpen, Loader2, AlertTriangle, } from "lucide-react"; import { Button } from "@/components/ui/button"; const RELEASES_URL = "https://github.com/farion1231/cc-switch/releases"; interface DatabaseUpgradeProps { payload: { path?: string; error?: string; kind?: string; db_version?: number; supported_version?: number; }; } // checking: 启动时检查是否有可用更新 // upgradable: 有可用更新,升级应用即可解决 // incompatible: 已是最新版本但数据库仍过新(可能来自第三方客户端),升级无法解决 // updating: 正在下载/安装更新 // error: 升级过程出错 type Phase = "checking" | "upgradable" | "incompatible" | "updating" | "error"; interface DownloadProgress { downloaded: number; total: number | null; } /** * 数据库版本过新(应用过旧)时的应用内恢复界面。 * * 启动时先检查是否有可用更新: * - 有 → 提供「升级应用」一键下载+安装+重启,并展示下载进度条。 * - 无 → 说明当前已是最新版本但数据库仍不兼容(通常由第三方客户端或更高版本创建), * 升级无法解决,及时提醒用户备份后改用兼容客户端或等待官方支持。 */ export function DatabaseUpgrade({ payload }: DatabaseUpgradeProps) { const { t } = useTranslation(); const [phase, setPhase] = useState("checking"); const [availableVersion, setAvailableVersion] = useState(null); const [progress, setProgress] = useState(null); const [errorMsg, setErrorMsg] = useState(null); const unlistenRef = useRef<(() => void) | null>(null); const dbVersion = payload.db_version; const supportedVersion = payload.supported_version; // 启动时检查可用更新,决定 upgradable / incompatible useEffect(() => { let cancelled = false; (async () => { try { const version = await invoke( "check_app_update_available", ); if (cancelled) return; if (version) { setAvailableVersion(version); setPhase("upgradable"); } else { setPhase("incompatible"); } } catch { // 检查失败(如离线):仍允许尝试升级,避免完全卡死 if (!cancelled) setPhase("upgradable"); } })(); return () => { cancelled = true; }; }, []); useEffect(() => { return () => { unlistenRef.current?.(); }; }, []); const startUpgrade = useCallback(async () => { setPhase("updating"); setProgress(null); setErrorMsg(null); try { unlistenRef.current?.(); unlistenRef.current = await listen( "update-download-progress", (e) => setProgress(e.payload), ); // 成功时后端会下载+安装+重启,不会返回;返回 false 表示无可用更新。 const updating = await invoke("install_update_and_restart"); unlistenRef.current?.(); unlistenRef.current = null; if (!updating) { // 竞态:检查时有更新、安装时已无 → 按不兼容处理 setPhase("incompatible"); } // updating === true:应用即将重启,保持 updating 态直到进程退出。 } catch (e) { unlistenRef.current?.(); unlistenRef.current = null; setErrorMsg(e instanceof Error ? e.message : String(e)); setPhase("error"); } }, []); const percent = progress && progress.total && progress.total > 0 ? Math.min(100, Math.round((progress.downloaded / progress.total) * 100)) : null; const fmtMB = (n: number) => (n / 1024 / 1024).toFixed(1); const isIncompatible = phase === "incompatible"; const accent = isIncompatible ? { chip: "bg-red-100 text-red-600 dark:bg-red-950/50 dark:text-red-400", Icon: AlertTriangle, } : { chip: "bg-amber-100 text-amber-600 dark:bg-amber-950/50 dark:text-amber-400", Icon: Database, }; const AccentIcon = accent.Icon; return (

{t("dbUpgrade.title", "数据库版本过新")}

{t( "dbUpgrade.description", "当前数据库由更新版本的 CC Switch 创建,需要升级应用后才能继续使用。升级不会删除你的数据。", )}

{dbVersion != null && supportedVersion != null && (

{t("dbUpgrade.versionInfo", { db: dbVersion, supported: supportedVersion, defaultValue: "数据库版本 v{{db}} · 应用支持 v{{supported}}", })}

)}
{/* 错误详情 / 数据库路径 */}
{payload.error && (

{payload.error}

)} {payload.path && (

{t("dbUpgrade.dbPath", "数据库文件")}:{payload.path}

)}
{phase === "checking" && (

{t("dbUpgrade.checking", "正在检查可用更新…")}

)} {phase === "upgradable" && availableVersion && (

{t("dbUpgrade.updateAvailable", { version: availableVersion, defaultValue: "发现新版本 v{{version}},升级后即可继续使用。", })}

)} {phase === "incompatible" && (

{t("dbUpgrade.incompatibleTitle", "升级也无法解决")}

{t("dbUpgrade.incompatibleDescription", { db: dbVersion, supported: supportedVersion, defaultValue: "你已是最新版本,但数据库版本(v{{db}})仍高于本应用支持的版本(v{{supported}})。该数据库可能由第三方客户端或更高版本创建,升级当前官方应用也无法兼容。", })}

)} {phase === "updating" && (
{percent === null ? t("dbUpgrade.preparing", "正在准备更新…") : t("dbUpgrade.downloading", "正在下载更新…")} {percent !== null && ( {percent}% )}
{progress && (

{fmtMB(progress.downloaded)} MB {progress.total ? ` / ${fmtMB(progress.total)} MB` : ""}

)}
)} {phase === "error" && errorMsg && (

{errorMsg}

)}
{(phase === "upgradable" || phase === "error") && ( )} {(phase === "incompatible" || phase === "error") && ( )}
); } export default DatabaseUpgrade;