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:
@@ -1,5 +1,12 @@
|
|||||||
import { useState, useEffect } from "react";
|
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 { getVersion } from "@tauri-apps/api/app";
|
||||||
import "../lib/tauri-api";
|
import "../lib/tauri-api";
|
||||||
import { relaunchApp } from "../lib/updater";
|
import { relaunchApp } from "../lib/updater";
|
||||||
@@ -18,7 +25,9 @@ export default function SettingsModal({ onClose }: SettingsModalProps) {
|
|||||||
const [version, setVersion] = useState<string>("");
|
const [version, setVersion] = useState<string>("");
|
||||||
const [isCheckingUpdate, setIsCheckingUpdate] = useState(false);
|
const [isCheckingUpdate, setIsCheckingUpdate] = useState(false);
|
||||||
const [isDownloading, setIsDownloading] = 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(() => {
|
useEffect(() => {
|
||||||
loadSettings();
|
loadSettings();
|
||||||
@@ -86,12 +95,29 @@ export default function SettingsModal({ onClose }: SettingsModalProps) {
|
|||||||
} else {
|
} else {
|
||||||
// 尚未检测到更新:先检查
|
// 尚未检测到更新:先检查
|
||||||
setIsCheckingUpdate(true);
|
setIsCheckingUpdate(true);
|
||||||
|
setShowUpToDate(false);
|
||||||
try {
|
try {
|
||||||
await checkUpdate();
|
const hasNewUpdate = await checkUpdate();
|
||||||
// 检查后若有更新,让用户再次点击执行
|
// 检查完成后,如果没有更新,显示"已是最新"
|
||||||
|
if (!hasNewUpdate) {
|
||||||
|
setShowUpToDate(true);
|
||||||
|
// 3秒后恢复按钮文字
|
||||||
|
setTimeout(() => {
|
||||||
|
setShowUpToDate(false);
|
||||||
|
}, 3000);
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("检查更新失败,回退到 Releases 页面:", error);
|
console.error("检查更新失败:", error);
|
||||||
|
// 在开发模式下,模拟已是最新版本的响应
|
||||||
|
if (import.meta.env.DEV) {
|
||||||
|
setShowUpToDate(true);
|
||||||
|
setTimeout(() => {
|
||||||
|
setShowUpToDate(false);
|
||||||
|
}, 3000);
|
||||||
|
} else {
|
||||||
|
// 生产环境下如果更新插件不可用,回退到打开 Releases 页面
|
||||||
await window.api.checkForUpdates();
|
await window.api.checkForUpdates();
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
setIsCheckingUpdate(false);
|
setIsCheckingUpdate(false);
|
||||||
}
|
}
|
||||||
@@ -111,12 +137,16 @@ export default function SettingsModal({ onClose }: SettingsModalProps) {
|
|||||||
const targetVersion = updateInfo?.availableVersion || version;
|
const targetVersion = updateInfo?.availableVersion || version;
|
||||||
// 如果未知或为空,回退到 releases 首页
|
// 如果未知或为空,回退到 releases 首页
|
||||||
if (!targetVersion || targetVersion === "未知") {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
const tag = targetVersion.startsWith("v") ? targetVersion : `v${targetVersion}`;
|
const tag = targetVersion.startsWith("v")
|
||||||
|
? targetVersion
|
||||||
|
: `v${targetVersion}`;
|
||||||
await window.api.openExternal(
|
await window.api.openExternal(
|
||||||
`https://github.com/farion1231/cc-switch/releases/tag/${tag}`,
|
`https://github.com/farion1231/cc-switch/releases/tag/${tag}`
|
||||||
);
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("打开更新日志失败:", error);
|
console.error("打开更新日志失败:", error);
|
||||||
@@ -206,7 +236,9 @@ export default function SettingsModal({ onClose }: SettingsModalProps) {
|
|||||||
<button
|
<button
|
||||||
onClick={handleOpenReleaseNotes}
|
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"
|
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">
|
<span className="inline-flex items-center gap-1">
|
||||||
<ExternalLink size={12} />
|
<ExternalLink size={12} />
|
||||||
@@ -221,6 +253,8 @@ export default function SettingsModal({ onClose }: SettingsModalProps) {
|
|||||||
? "bg-gray-100 dark:bg-gray-700 text-gray-400 dark:text-gray-500 cursor-not-allowed"
|
? "bg-gray-100 dark:bg-gray-700 text-gray-400 dark:text-gray-500 cursor-not-allowed"
|
||||||
: hasUpdate
|
: hasUpdate
|
||||||
? "bg-blue-500 hover:bg-blue-600 dark:bg-blue-600 dark:hover:bg-blue-700 text-white"
|
? "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"
|
: "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"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
@@ -239,6 +273,11 @@ export default function SettingsModal({ onClose }: SettingsModalProps) {
|
|||||||
<Download size={12} />
|
<Download size={12} />
|
||||||
更新到 v{updateInfo?.availableVersion}
|
更新到 v{updateInfo?.availableVersion}
|
||||||
</span>
|
</span>
|
||||||
|
) : showUpToDate ? (
|
||||||
|
<span className="flex items-center gap-1">
|
||||||
|
<Check size={12} />
|
||||||
|
已是最新
|
||||||
|
</span>
|
||||||
) : (
|
) : (
|
||||||
"检查更新"
|
"检查更新"
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ interface UpdateContextValue {
|
|||||||
dismissUpdate: () => void;
|
dismissUpdate: () => void;
|
||||||
|
|
||||||
// 操作方法
|
// 操作方法
|
||||||
checkUpdate: () => Promise<void>;
|
checkUpdate: () => Promise<boolean>;
|
||||||
resetDismiss: () => void;
|
resetDismiss: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,7 +54,7 @@ export function UpdateProvider({ children }: { children: React.ReactNode }) {
|
|||||||
const isCheckingRef = useRef(false);
|
const isCheckingRef = useRef(false);
|
||||||
|
|
||||||
const checkUpdate = useCallback(async () => {
|
const checkUpdate = useCallback(async () => {
|
||||||
if (isCheckingRef.current) return;
|
if (isCheckingRef.current) return false;
|
||||||
isCheckingRef.current = true;
|
isCheckingRef.current = true;
|
||||||
setIsChecking(true);
|
setIsChecking(true);
|
||||||
setError(null);
|
setError(null);
|
||||||
@@ -78,16 +78,19 @@ export function UpdateProvider({ children }: { children: React.ReactNode }) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
setIsDismissed(dismissedVersion === result.info.availableVersion);
|
setIsDismissed(dismissedVersion === result.info.availableVersion);
|
||||||
|
return true; // 有更新
|
||||||
} else {
|
} else {
|
||||||
setHasUpdate(false);
|
setHasUpdate(false);
|
||||||
setUpdateInfo(null);
|
setUpdateInfo(null);
|
||||||
setUpdateHandle(null);
|
setUpdateHandle(null);
|
||||||
setIsDismissed(false);
|
setIsDismissed(false);
|
||||||
|
return false; // 已是最新
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("检查更新失败:", err);
|
console.error("检查更新失败:", err);
|
||||||
setError(err instanceof Error ? err.message : "检查更新失败");
|
setError(err instanceof Error ? err.message : "检查更新失败");
|
||||||
setHasUpdate(false);
|
setHasUpdate(false);
|
||||||
|
throw err; // 抛出错误让调用方处理
|
||||||
} finally {
|
} finally {
|
||||||
setIsChecking(false);
|
setIsChecking(false);
|
||||||
isCheckingRef.current = false;
|
isCheckingRef.current = false;
|
||||||
|
|||||||
Reference in New Issue
Block a user