feat(ui): add "up-to-date" feedback for update check button

- Show green "Already up-to-date" state with check icon when no updates available
- Button changes color and text temporarily (3 seconds) to provide clear feedback
- Fix TypeScript type for checkUpdate to return Promise<boolean>
- Handle dev mode gracefully - show up-to-date instead of opening release page
- Simplify previous complex notification UI to inline button state change
This commit is contained in:
farion1231
2025-09-11 15:13:33 +08:00
parent 54b88d9c89
commit c597b9b122
2 changed files with 56 additions and 14 deletions

View File

@@ -1,5 +1,12 @@
import { useState, useEffect } from "react";
import { X, RefreshCw, FolderOpen, Download, ExternalLink } from "lucide-react";
import {
X,
RefreshCw,
FolderOpen,
Download,
ExternalLink,
Check,
} from "lucide-react";
import { getVersion } from "@tauri-apps/api/app";
import "../lib/tauri-api";
import { relaunchApp } from "../lib/updater";
@@ -18,7 +25,9 @@ export default function SettingsModal({ onClose }: SettingsModalProps) {
const [version, setVersion] = useState<string>("");
const [isCheckingUpdate, setIsCheckingUpdate] = useState(false);
const [isDownloading, setIsDownloading] = useState(false);
const { hasUpdate, updateInfo, updateHandle, checkUpdate, resetDismiss } = useUpdate();
const [showUpToDate, setShowUpToDate] = useState(false);
const { hasUpdate, updateInfo, updateHandle, checkUpdate, resetDismiss } =
useUpdate();
useEffect(() => {
loadSettings();
@@ -86,12 +95,29 @@ export default function SettingsModal({ onClose }: SettingsModalProps) {
} else {
// 尚未检测到更新:先检查
setIsCheckingUpdate(true);
setShowUpToDate(false);
try {
await checkUpdate();
// 检查后若有更新,让用户再次点击执行
const hasNewUpdate = await checkUpdate();
// 检查完成后,如果没有更新,显示"已是最新"
if (!hasNewUpdate) {
setShowUpToDate(true);
// 3秒后恢复按钮文字
setTimeout(() => {
setShowUpToDate(false);
}, 3000);
}
} catch (error) {
console.error("检查更新失败,回退到 Releases 页面:", error);
await window.api.checkForUpdates();
console.error("检查更新失败:", error);
// 在开发模式下,模拟已是最新版本的响应
if (import.meta.env.DEV) {
setShowUpToDate(true);
setTimeout(() => {
setShowUpToDate(false);
}, 3000);
} else {
// 生产环境下如果更新插件不可用,回退到打开 Releases 页面
await window.api.checkForUpdates();
}
} finally {
setIsCheckingUpdate(false);
}
@@ -111,12 +137,16 @@ export default function SettingsModal({ onClose }: SettingsModalProps) {
const targetVersion = updateInfo?.availableVersion || version;
// 如果未知或为空,回退到 releases 首页
if (!targetVersion || targetVersion === "未知") {
await window.api.openExternal("https://github.com/farion1231/cc-switch/releases");
await window.api.openExternal(
"https://github.com/farion1231/cc-switch/releases"
);
return;
}
const tag = targetVersion.startsWith("v") ? targetVersion : `v${targetVersion}`;
const tag = targetVersion.startsWith("v")
? targetVersion
: `v${targetVersion}`;
await window.api.openExternal(
`https://github.com/farion1231/cc-switch/releases/tag/${tag}`,
`https://github.com/farion1231/cc-switch/releases/tag/${tag}`
);
} catch (error) {
console.error("打开更新日志失败:", error);
@@ -206,7 +236,9 @@ export default function SettingsModal({ onClose }: SettingsModalProps) {
<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"
title={hasUpdate ? "查看该版本更新日志" : "查看当前版本更新日志"}
title={
hasUpdate ? "查看该版本更新日志" : "查看当前版本更新日志"
}
>
<span className="inline-flex items-center gap-1">
<ExternalLink size={12} />
@@ -220,8 +252,10 @@ export default function SettingsModal({ onClose }: SettingsModalProps) {
isCheckingUpdate || isDownloading
? "bg-gray-100 dark:bg-gray-700 text-gray-400 dark:text-gray-500 cursor-not-allowed"
: hasUpdate
? "bg-blue-500 hover:bg-blue-600 dark:bg-blue-600 dark:hover:bg-blue-700 text-white"
: "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"
? "bg-blue-500 hover:bg-blue-600 dark:bg-blue-600 dark:hover:bg-blue-700 text-white"
: 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"
}`}
>
{isDownloading ? (
@@ -239,6 +273,11 @@ export default function SettingsModal({ onClose }: SettingsModalProps) {
<Download size={12} />
v{updateInfo?.availableVersion}
</span>
) : showUpToDate ? (
<span className="flex items-center gap-1">
<Check size={12} />
</span>
) : (
"检查更新"
)}

View File

@@ -15,7 +15,7 @@ interface UpdateContextValue {
dismissUpdate: () => void;
// 操作方法
checkUpdate: () => Promise<void>;
checkUpdate: () => Promise<boolean>;
resetDismiss: () => void;
}
@@ -54,7 +54,7 @@ export function UpdateProvider({ children }: { children: React.ReactNode }) {
const isCheckingRef = useRef(false);
const checkUpdate = useCallback(async () => {
if (isCheckingRef.current) return;
if (isCheckingRef.current) return false;
isCheckingRef.current = true;
setIsChecking(true);
setError(null);
@@ -78,16 +78,19 @@ export function UpdateProvider({ children }: { children: React.ReactNode }) {
}
}
setIsDismissed(dismissedVersion === result.info.availableVersion);
return true; // 有更新
} else {
setHasUpdate(false);
setUpdateInfo(null);
setUpdateHandle(null);
setIsDismissed(false);
return false; // 已是最新
}
} catch (err) {
console.error("检查更新失败:", err);
setError(err instanceof Error ? err.message : "检查更新失败");
setHasUpdate(false);
throw err; // 抛出错误让调用方处理
} finally {
setIsChecking(false);
isCheckingRef.current = false;