2025-09-07 10:48:27 +08:00
|
|
|
|
import { useState, useEffect } from "react";
|
2025-09-11 15:13:33 +08:00
|
|
|
|
import {
|
|
|
|
|
|
X,
|
|
|
|
|
|
RefreshCw,
|
|
|
|
|
|
FolderOpen,
|
|
|
|
|
|
Download,
|
|
|
|
|
|
ExternalLink,
|
|
|
|
|
|
Check,
|
|
|
|
|
|
} from "lucide-react";
|
2025-09-07 22:14:17 +08:00
|
|
|
|
import { getVersion } from "@tauri-apps/api/app";
|
2025-09-07 10:48:27 +08:00
|
|
|
|
import "../lib/tauri-api";
|
2025-09-10 19:46:38 +08:00
|
|
|
|
import { relaunchApp } from "../lib/updater";
|
|
|
|
|
|
import { useUpdate } from "../contexts/UpdateContext";
|
2025-09-07 11:36:09 +08:00
|
|
|
|
import type { Settings } from "../types";
|
2025-09-07 10:48:27 +08:00
|
|
|
|
|
|
|
|
|
|
interface SettingsModalProps {
|
|
|
|
|
|
onClose: () => void;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export default function SettingsModal({ onClose }: SettingsModalProps) {
|
|
|
|
|
|
const [settings, setSettings] = useState<Settings>({
|
2025-09-11 20:20:27 +08:00
|
|
|
|
showInTray: true,
|
2025-09-07 10:48:27 +08:00
|
|
|
|
});
|
|
|
|
|
|
const [configPath, setConfigPath] = useState<string>("");
|
2025-09-07 22:14:17 +08:00
|
|
|
|
const [version, setVersion] = useState<string>("");
|
2025-09-07 10:48:27 +08:00
|
|
|
|
const [isCheckingUpdate, setIsCheckingUpdate] = useState(false);
|
2025-09-10 19:46:38 +08:00
|
|
|
|
const [isDownloading, setIsDownloading] = useState(false);
|
2025-09-11 15:13:33 +08:00
|
|
|
|
const [showUpToDate, setShowUpToDate] = useState(false);
|
|
|
|
|
|
const { hasUpdate, updateInfo, updateHandle, checkUpdate, resetDismiss } =
|
|
|
|
|
|
useUpdate();
|
2025-09-07 10:48:27 +08:00
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
|
loadSettings();
|
|
|
|
|
|
loadConfigPath();
|
2025-09-07 22:14:17 +08:00
|
|
|
|
loadVersion();
|
2025-09-07 10:48:27 +08:00
|
|
|
|
}, []);
|
|
|
|
|
|
|
2025-09-07 22:14:17 +08:00
|
|
|
|
const loadVersion = async () => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
const appVersion = await getVersion();
|
|
|
|
|
|
setVersion(appVersion);
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error("获取版本信息失败:", error);
|
2025-09-10 19:46:38 +08:00
|
|
|
|
// 失败时不硬编码版本号,显示为未知
|
|
|
|
|
|
setVersion("未知");
|
2025-09-07 22:14:17 +08:00
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2025-09-07 10:48:27 +08:00
|
|
|
|
const loadSettings = async () => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
const loadedSettings = await window.api.getSettings();
|
2025-09-11 20:20:27 +08:00
|
|
|
|
if ((loadedSettings as any)?.showInTray !== undefined) {
|
|
|
|
|
|
setSettings({ showInTray: (loadedSettings as any).showInTray });
|
|
|
|
|
|
} else if ((loadedSettings as any)?.showInDock !== undefined) {
|
|
|
|
|
|
// 向后兼容:若历史上有 showInDock,则映射为 showInTray
|
|
|
|
|
|
setSettings({ showInTray: (loadedSettings as any).showInDock });
|
2025-09-07 10:48:27 +08:00
|
|
|
|
}
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error("加载设置失败:", error);
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const loadConfigPath = async () => {
|
|
|
|
|
|
try {
|
2025-09-07 11:36:09 +08:00
|
|
|
|
const path = await window.api.getAppConfigPath();
|
|
|
|
|
|
if (path) {
|
|
|
|
|
|
setConfigPath(path);
|
2025-09-07 10:48:27 +08:00
|
|
|
|
}
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error("获取配置路径失败:", error);
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const saveSettings = async () => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
await window.api.saveSettings(settings);
|
|
|
|
|
|
onClose();
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error("保存设置失败:", error);
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const handleCheckUpdate = async () => {
|
2025-09-10 19:46:38 +08:00
|
|
|
|
if (hasUpdate && updateHandle) {
|
|
|
|
|
|
// 已检测到更新:直接复用 updateHandle 下载并安装,避免重复检查
|
|
|
|
|
|
setIsDownloading(true);
|
|
|
|
|
|
try {
|
|
|
|
|
|
resetDismiss();
|
|
|
|
|
|
await updateHandle.downloadAndInstall();
|
|
|
|
|
|
await relaunchApp();
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error("更新失败:", error);
|
|
|
|
|
|
// 更新失败时回退到打开 Releases 页面
|
|
|
|
|
|
await window.api.checkForUpdates();
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
setIsDownloading(false);
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// 尚未检测到更新:先检查
|
|
|
|
|
|
setIsCheckingUpdate(true);
|
2025-09-11 15:13:33 +08:00
|
|
|
|
setShowUpToDate(false);
|
2025-09-10 19:46:38 +08:00
|
|
|
|
try {
|
2025-09-11 15:13:33 +08:00
|
|
|
|
const hasNewUpdate = await checkUpdate();
|
|
|
|
|
|
// 检查完成后,如果没有更新,显示"已是最新"
|
|
|
|
|
|
if (!hasNewUpdate) {
|
|
|
|
|
|
setShowUpToDate(true);
|
|
|
|
|
|
// 3秒后恢复按钮文字
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
|
setShowUpToDate(false);
|
|
|
|
|
|
}, 3000);
|
|
|
|
|
|
}
|
2025-09-10 19:46:38 +08:00
|
|
|
|
} catch (error) {
|
2025-09-11 15:13:33 +08:00
|
|
|
|
console.error("检查更新失败:", error);
|
|
|
|
|
|
// 在开发模式下,模拟已是最新版本的响应
|
|
|
|
|
|
if (import.meta.env.DEV) {
|
|
|
|
|
|
setShowUpToDate(true);
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
|
setShowUpToDate(false);
|
|
|
|
|
|
}, 3000);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// 生产环境下如果更新插件不可用,回退到打开 Releases 页面
|
|
|
|
|
|
await window.api.checkForUpdates();
|
|
|
|
|
|
}
|
2025-09-10 19:46:38 +08:00
|
|
|
|
} finally {
|
|
|
|
|
|
setIsCheckingUpdate(false);
|
|
|
|
|
|
}
|
2025-09-07 10:48:27 +08:00
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const handleOpenConfigFolder = async () => {
|
|
|
|
|
|
try {
|
2025-09-07 11:36:09 +08:00
|
|
|
|
await window.api.openAppConfigFolder();
|
2025-09-07 10:48:27 +08:00
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error("打开配置文件夹失败:", error);
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2025-09-10 19:46:38 +08:00
|
|
|
|
const handleOpenReleaseNotes = async () => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
const targetVersion = updateInfo?.availableVersion || version;
|
|
|
|
|
|
// 如果未知或为空,回退到 releases 首页
|
|
|
|
|
|
if (!targetVersion || targetVersion === "未知") {
|
2025-09-11 15:13:33 +08:00
|
|
|
|
await window.api.openExternal(
|
2025-09-13 15:36:43 +08:00
|
|
|
|
"https://github.com/farion1231/cc-switch/releases",
|
2025-09-11 15:13:33 +08:00
|
|
|
|
);
|
2025-09-10 19:46:38 +08:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
2025-09-11 15:13:33 +08:00
|
|
|
|
const tag = targetVersion.startsWith("v")
|
|
|
|
|
|
? targetVersion
|
|
|
|
|
|
: `v${targetVersion}`;
|
2025-09-10 19:46:38 +08:00
|
|
|
|
await window.api.openExternal(
|
2025-09-13 15:36:43 +08:00
|
|
|
|
`https://github.com/farion1231/cc-switch/releases/tag/${tag}`,
|
2025-09-10 19:46:38 +08:00
|
|
|
|
);
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error("打开更新日志失败:", error);
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2025-09-07 10:48:27 +08:00
|
|
|
|
return (
|
2025-09-16 15:50:16 +08:00
|
|
|
|
<div
|
|
|
|
|
|
className="fixed inset-0 z-50 flex items-center justify-center"
|
|
|
|
|
|
onMouseDown={(e) => {
|
|
|
|
|
|
if (e.target === e.currentTarget) onClose();
|
|
|
|
|
|
}}
|
|
|
|
|
|
>
|
|
|
|
|
|
<div className="absolute inset-0 bg-black/50 dark:bg-black/70 backdrop-blur-sm" />
|
|
|
|
|
|
<div className="relative bg-white dark:bg-gray-900 rounded-xl shadow-2xl w-[500px] overflow-hidden">
|
2025-09-07 10:48:27 +08:00
|
|
|
|
{/* 标题栏 */}
|
2025-09-08 15:38:06 +08:00
|
|
|
|
<div className="flex items-center justify-between px-6 py-4 border-b border-gray-200 dark:border-gray-800">
|
|
|
|
|
|
<h2 className="text-lg font-semibold text-blue-500 dark:text-blue-400">
|
2025-09-07 10:48:27 +08:00
|
|
|
|
设置
|
|
|
|
|
|
</h2>
|
|
|
|
|
|
<button
|
|
|
|
|
|
onClick={onClose}
|
2025-09-08 15:38:06 +08:00
|
|
|
|
className="p-1.5 hover:bg-gray-100 dark:hover:bg-gray-800 rounded-md transition-colors"
|
2025-09-07 10:48:27 +08:00
|
|
|
|
>
|
2025-09-08 15:38:06 +08:00
|
|
|
|
<X size={20} className="text-gray-500 dark:text-gray-400" />
|
2025-09-07 10:48:27 +08:00
|
|
|
|
</button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
{/* 设置内容 */}
|
|
|
|
|
|
<div className="px-6 py-4 space-y-6">
|
2025-09-11 20:20:27 +08:00
|
|
|
|
{/* 系统托盘设置(未实现)
|
|
|
|
|
|
说明:此开关用于控制是否在系统托盘/菜单栏显示应用图标。 */}
|
2025-09-07 22:14:17 +08:00
|
|
|
|
{/* <div>
|
2025-09-08 15:38:06 +08:00
|
|
|
|
<h3 className="text-sm font-medium text-gray-900 dark:text-gray-100 mb-3">
|
2025-09-11 20:20:27 +08:00
|
|
|
|
显示设置(系统托盘)
|
2025-09-07 10:48:27 +08:00
|
|
|
|
</h3>
|
|
|
|
|
|
<label className="flex items-center justify-between">
|
2025-09-08 11:48:05 +08:00
|
|
|
|
<span className="text-sm text-gray-500">
|
2025-09-11 20:20:27 +08:00
|
|
|
|
在菜单栏显示图标(系统托盘)
|
2025-09-07 10:48:27 +08:00
|
|
|
|
</span>
|
|
|
|
|
|
<input
|
|
|
|
|
|
type="checkbox"
|
2025-09-11 20:20:27 +08:00
|
|
|
|
checked={settings.showInTray}
|
2025-09-07 10:48:27 +08:00
|
|
|
|
onChange={(e) =>
|
2025-09-11 20:20:27 +08:00
|
|
|
|
setSettings({ ...settings, showInTray: e.target.checked })
|
2025-09-07 10:48:27 +08:00
|
|
|
|
}
|
2025-09-08 11:48:05 +08:00
|
|
|
|
className="w-4 h-4 text-blue-500 rounded focus:ring-blue-500/20"
|
2025-09-07 10:48:27 +08:00
|
|
|
|
/>
|
|
|
|
|
|
</label>
|
2025-09-07 22:14:17 +08:00
|
|
|
|
</div> */}
|
2025-09-07 10:48:27 +08:00
|
|
|
|
|
|
|
|
|
|
{/* 配置文件位置 */}
|
|
|
|
|
|
<div>
|
2025-09-08 15:38:06 +08:00
|
|
|
|
<h3 className="text-sm font-medium text-gray-900 dark:text-gray-100 mb-3">
|
2025-09-07 10:48:27 +08:00
|
|
|
|
配置文件位置
|
|
|
|
|
|
</h3>
|
|
|
|
|
|
<div className="flex items-center gap-2">
|
2025-09-08 15:38:06 +08:00
|
|
|
|
<div className="flex-1 px-3 py-2 bg-gray-100 dark:bg-gray-800 rounded-lg">
|
|
|
|
|
|
<span className="text-xs font-mono text-gray-500 dark:text-gray-400">
|
2025-09-07 10:48:27 +08:00
|
|
|
|
{configPath || "加载中..."}
|
|
|
|
|
|
</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<button
|
|
|
|
|
|
onClick={handleOpenConfigFolder}
|
2025-09-08 15:38:06 +08:00
|
|
|
|
className="p-2 hover:bg-gray-100 dark:hover:bg-gray-800 rounded-lg transition-colors"
|
2025-09-07 10:48:27 +08:00
|
|
|
|
title="打开文件夹"
|
|
|
|
|
|
>
|
|
|
|
|
|
<FolderOpen
|
|
|
|
|
|
size={18}
|
2025-09-08 15:38:06 +08:00
|
|
|
|
className="text-gray-500 dark:text-gray-400"
|
2025-09-07 10:48:27 +08:00
|
|
|
|
/>
|
|
|
|
|
|
</button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
{/* 关于 */}
|
|
|
|
|
|
<div>
|
2025-09-08 15:38:06 +08:00
|
|
|
|
<h3 className="text-sm font-medium text-gray-900 dark:text-gray-100 mb-3">
|
2025-09-07 10:48:27 +08:00
|
|
|
|
关于
|
|
|
|
|
|
</h3>
|
2025-09-08 15:38:06 +08:00
|
|
|
|
<div className="p-4 bg-gray-100 dark:bg-gray-800 rounded-lg">
|
2025-09-07 10:48:27 +08:00
|
|
|
|
<div className="flex items-start justify-between">
|
2025-09-11 12:06:49 +08:00
|
|
|
|
<div>
|
2025-09-07 10:48:27 +08:00
|
|
|
|
<div className="text-sm">
|
2025-09-08 15:38:06 +08:00
|
|
|
|
<p className="font-medium text-gray-900 dark:text-gray-100">
|
2025-09-07 10:48:27 +08:00
|
|
|
|
CC Switch
|
|
|
|
|
|
</p>
|
2025-09-08 15:38:06 +08:00
|
|
|
|
<p className="mt-1 text-gray-500 dark:text-gray-400">
|
2025-09-07 10:48:27 +08:00
|
|
|
|
版本 {version}
|
|
|
|
|
|
</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
2025-09-10 19:46:38 +08:00
|
|
|
|
<div className="flex items-center gap-2">
|
|
|
|
|
|
<button
|
|
|
|
|
|
onClick={handleOpenReleaseNotes}
|
|
|
|
|
|
className="px-2 py-1 text-xs font-medium text-blue-500 hover:text-blue-600 dark:text-blue-400 dark:hover:text-blue-300 rounded-lg hover:bg-blue-500/10 transition-colors"
|
2025-09-11 15:13:33 +08:00
|
|
|
|
title={
|
|
|
|
|
|
hasUpdate ? "查看该版本更新日志" : "查看当前版本更新日志"
|
|
|
|
|
|
}
|
2025-09-10 19:46:38 +08:00
|
|
|
|
>
|
|
|
|
|
|
<span className="inline-flex items-center gap-1">
|
|
|
|
|
|
<ExternalLink size={12} />
|
|
|
|
|
|
更新日志
|
2025-09-07 10:48:27 +08:00
|
|
|
|
</span>
|
2025-09-10 19:46:38 +08:00
|
|
|
|
</button>
|
|
|
|
|
|
<button
|
|
|
|
|
|
onClick={handleCheckUpdate}
|
|
|
|
|
|
disabled={isCheckingUpdate || isDownloading}
|
2025-09-16 16:23:47 +08:00
|
|
|
|
className={`min-w-[88px] px-3 py-1.5 text-xs font-medium rounded-lg transition-all ${
|
2025-09-10 19:46:38 +08:00
|
|
|
|
isCheckingUpdate || isDownloading
|
2025-09-16 16:23:47 +08:00
|
|
|
|
? "bg-gray-100 dark:bg-gray-700 text-gray-400 dark:text-gray-500 cursor-not-allowed border border-transparent"
|
2025-09-10 19:46:38 +08:00
|
|
|
|
: hasUpdate
|
2025-09-16 16:23:47 +08:00
|
|
|
|
? "bg-blue-500 hover:bg-blue-600 dark:bg-blue-600 dark:hover:bg-blue-700 text-white border border-transparent"
|
2025-09-11 15:13:33 +08:00
|
|
|
|
: showUpToDate
|
|
|
|
|
|
? "bg-green-50 dark:bg-green-900/20 text-green-600 dark:text-green-400 border border-green-200 dark:border-green-800"
|
|
|
|
|
|
: "bg-white dark:bg-gray-700 hover:bg-gray-50 dark:hover:bg-gray-600 text-blue-500 dark:text-blue-400 border border-gray-200 dark:border-gray-600"
|
2025-09-10 19:46:38 +08:00
|
|
|
|
}`}
|
|
|
|
|
|
>
|
|
|
|
|
|
{isDownloading ? (
|
|
|
|
|
|
<span className="flex items-center gap-1">
|
|
|
|
|
|
<Download size={12} className="animate-pulse" />
|
|
|
|
|
|
更新中...
|
|
|
|
|
|
</span>
|
|
|
|
|
|
) : isCheckingUpdate ? (
|
|
|
|
|
|
<span className="flex items-center gap-1">
|
|
|
|
|
|
<RefreshCw size={12} className="animate-spin" />
|
|
|
|
|
|
检查中...
|
|
|
|
|
|
</span>
|
|
|
|
|
|
) : hasUpdate ? (
|
|
|
|
|
|
<span className="flex items-center gap-1">
|
|
|
|
|
|
<Download size={12} />
|
2025-09-11 12:06:49 +08:00
|
|
|
|
更新到 v{updateInfo?.availableVersion}
|
2025-09-10 19:46:38 +08:00
|
|
|
|
</span>
|
2025-09-11 15:13:33 +08:00
|
|
|
|
) : showUpToDate ? (
|
|
|
|
|
|
<span className="flex items-center gap-1">
|
|
|
|
|
|
<Check size={12} />
|
|
|
|
|
|
已是最新
|
|
|
|
|
|
</span>
|
2025-09-10 19:46:38 +08:00
|
|
|
|
) : (
|
|
|
|
|
|
"检查更新"
|
|
|
|
|
|
)}
|
|
|
|
|
|
</button>
|
|
|
|
|
|
</div>
|
2025-09-07 10:48:27 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
{/* 底部按钮 */}
|
2025-09-08 15:38:06 +08:00
|
|
|
|
<div className="flex justify-end gap-3 px-6 py-4 border-t border-gray-200 dark:border-gray-800">
|
2025-09-07 10:48:27 +08:00
|
|
|
|
<button
|
|
|
|
|
|
onClick={onClose}
|
2025-09-08 15:38:06 +08:00
|
|
|
|
className="px-4 py-2 text-sm font-medium text-gray-500 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-800 rounded-lg transition-colors"
|
2025-09-07 10:48:27 +08:00
|
|
|
|
>
|
|
|
|
|
|
取消
|
|
|
|
|
|
</button>
|
|
|
|
|
|
<button
|
|
|
|
|
|
onClick={saveSettings}
|
2025-09-08 15:38:06 +08:00
|
|
|
|
className="px-4 py-2 text-sm font-medium text-white bg-blue-500 hover:bg-blue-600 dark:bg-blue-600 dark:hover:bg-blue-700 rounded-lg transition-colors"
|
2025-09-07 10:48:27 +08:00
|
|
|
|
>
|
|
|
|
|
|
保存
|
|
|
|
|
|
</button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|