优化更新失败时的提示语

修复检查更新按钮点击后有重影
This commit is contained in:
xintaofei
2026-03-08 08:35:50 +08:00
parent a16a230c44
commit ab31d3063e
5 changed files with 141 additions and 25 deletions

View File

@@ -33,6 +33,7 @@ import {
closeAppUpdate,
getCurrentAppVersion,
installAppUpdate,
normalizeAppUpdateError,
relaunchApp,
} from "@/lib/updater"
@@ -45,6 +46,8 @@ function isAppLocale(value: string): value is AppLocale {
return APP_LANGUAGE_VALUES.includes(value as AppLocale)
}
type UpdateAction = "check" | "install"
export function SystemNetworkSettings() {
const t = useTranslations("SystemSettings")
const tLanguage = useTranslations("Language")
@@ -172,6 +175,31 @@ export function SystemNetworkSettings() {
}
}, [appLanguage, languageSettings.language, setLanguageSettings, t])
const formatUpdateError = useCallback(
(error: unknown, action: UpdateAction): string => {
const { kind, rawMessage } = normalizeAppUpdateError(error)
switch (kind) {
case "source_unreachable":
return t("updateErrors.sourceUnavailable")
case "network":
return t("updateErrors.network")
case "download_failed":
return t("updateErrors.downloadFailed")
case "install_failed":
return t("updateErrors.installFailed")
case "unknown":
default:
if (action === "install") {
return t("updateErrors.installFailed")
}
console.error("[Settings] updater unknown error:", rawMessage)
return t("updateErrors.unknown")
}
},
[t]
)
const checkForUpdates = useCallback(async () => {
setCheckingUpdate(true)
setUpdateError(null)
@@ -194,13 +222,14 @@ export function SystemNetworkSettings() {
await closeAppUpdate(previousUpdate)
}
} catch (err) {
const message = err instanceof Error ? err.message : String(err)
const message = formatUpdateError(err, "check")
setUpdateError(message)
toast.error(t("checkUpdateFailed", { message }))
console.error("[Settings] check app update failed:", err)
} finally {
setCheckingUpdate(false)
}
}, [availableUpdate, t])
}, [availableUpdate, formatUpdateError, t])
const installUpdate = useCallback(async () => {
if (!availableUpdate) return
@@ -213,13 +242,14 @@ export function SystemNetworkSettings() {
toast.success(t("installSuccess"))
await relaunchApp()
} catch (err) {
const message = err instanceof Error ? err.message : String(err)
const message = formatUpdateError(err, "install")
setUpdateError(message)
toast.error(t("installFailed", { message }))
console.error("[Settings] install app update failed:", err)
} finally {
setInstallingUpdate(false)
}
}, [availableUpdate, t])
}, [availableUpdate, formatUpdateError, t])
if (loading) {
return (
@@ -393,24 +423,31 @@ export function SystemNetworkSettings() {
)}
<div className="flex flex-wrap items-center gap-2 justify-end">
{checkingUpdate ? (
<Button
key="checking-update"
size="sm"
variant="secondary"
disabled
aria-busy="true"
className="w-[9.5rem] justify-center transition-none"
>
<Loader2 className="h-3.5 w-3.5 animate-spin" />
{t("checking")}
</Button>
) : (
<Button
key="check-update"
size="sm"
variant="secondary"
onClick={checkForUpdates}
disabled={checkingUpdate || installingUpdate}
disabled={installingUpdate}
className="w-[9.5rem] justify-center transition-none"
>
{checkingUpdate ? (
<>
<Loader2 className="h-3.5 w-3.5 animate-spin" />
{t("checking")}
</>
) : (
<>
<RefreshCw className="h-3.5 w-3.5" />
{t("checkUpdate")}
</>
)}
</Button>
)}
{availableUpdate && (
<Button

View File

@@ -112,7 +112,14 @@
"alreadyLatest": "You're on the latest version",
"checkUpdateFailed": "Failed to check for updates: {message}",
"installSuccess": "Update installed. Relaunching app.",
"installFailed": "Update failed: {message}"
"installFailed": "Update failed: {message}",
"updateErrors": {
"sourceUnavailable": "Cannot reach the update source. Check your network or proxy and try again.",
"network": "Network connection failed. Check your network or proxy and try again.",
"downloadFailed": "Failed to download update package. Please try again later.",
"installFailed": "Failed to install update. Please close the app and try again.",
"unknown": "Update failed. Please try again later."
}
},
"ShortcutSettings": {
"sectionTitle": "Shortcuts",

View File

@@ -112,7 +112,14 @@
"alreadyLatest": "当前已经是最新版本",
"checkUpdateFailed": "检查更新失败:{message}",
"installSuccess": "升级包已安装,正在重启应用",
"installFailed": "升级失败:{message}"
"installFailed": "升级失败:{message}",
"updateErrors": {
"sourceUnavailable": "无法连接更新源,请检查网络或代理设置后重试。",
"network": "网络连接异常,请检查网络或代理设置后重试。",
"downloadFailed": "下载更新包失败,请稍后重试。",
"installFailed": "安装更新失败,请关闭应用后重试。",
"unknown": "更新失败,请稍后重试。"
}
},
"ShortcutSettings": {
"sectionTitle": "快捷键",

View File

@@ -112,7 +112,14 @@
"alreadyLatest": "目前已是最新版本",
"checkUpdateFailed": "檢查更新失敗:{message}",
"installSuccess": "升級包已安裝,正在重新啟動應用",
"installFailed": "升級失敗:{message}"
"installFailed": "升級失敗:{message}",
"updateErrors": {
"sourceUnavailable": "無法連線更新來源,請檢查網路或代理設定後重試。",
"network": "網路連線異常,請檢查網路或代理設定後重試。",
"downloadFailed": "下載更新包失敗,請稍後再試。",
"installFailed": "安裝更新失敗,請關閉應用後重試。",
"unknown": "更新失敗,請稍後再試。"
}
},
"ShortcutSettings": {
"sectionTitle": "快捷鍵",

View File

@@ -7,6 +7,18 @@ export interface AppUpdateCheckResult {
update: Update | null
}
export type AppUpdateErrorKind =
| "source_unreachable"
| "network"
| "download_failed"
| "install_failed"
| "unknown"
export interface AppUpdateErrorInfo {
kind: AppUpdateErrorKind
rawMessage: string
}
export async function getCurrentAppVersion(): Promise<string> {
return getVersion()
}
@@ -27,3 +39,49 @@ export async function relaunchApp(): Promise<void> {
export async function closeAppUpdate(update: Update): Promise<void> {
await update.close()
}
function toErrorMessage(error: unknown): string {
if (error instanceof Error) return error.message
return String(error)
}
export function normalizeAppUpdateError(error: unknown): AppUpdateErrorInfo {
const rawMessage = toErrorMessage(error)
const normalized = rawMessage.toLowerCase()
if (
normalized.includes("latest.json") ||
normalized.includes("/releases/latest/download/")
) {
return { kind: "source_unreachable", rawMessage }
}
if (
normalized.includes("error sending request for url") ||
normalized.includes("failed to send request") ||
normalized.includes("network") ||
normalized.includes("timed out") ||
normalized.includes("dns") ||
normalized.includes("connection refused")
) {
return { kind: "network", rawMessage }
}
if (
normalized.includes("download") ||
normalized.includes("checksum") ||
normalized.includes("content-length")
) {
return { kind: "download_failed", rawMessage }
}
if (
normalized.includes("install") ||
normalized.includes("installer") ||
normalized.includes("permission denied")
) {
return { kind: "install_failed", rawMessage }
}
return { kind: "unknown", rawMessage }
}