优化更新失败时的提示语
修复检查更新按钮点击后有重影
This commit is contained in:
@@ -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">
|
||||
<Button
|
||||
size="sm"
|
||||
variant="secondary"
|
||||
onClick={checkForUpdates}
|
||||
disabled={checkingUpdate || installingUpdate}
|
||||
>
|
||||
{checkingUpdate ? (
|
||||
<>
|
||||
<Loader2 className="h-3.5 w-3.5 animate-spin" />
|
||||
{t("checking")}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<RefreshCw className="h-3.5 w-3.5" />
|
||||
{t("checkUpdate")}
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
{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={installingUpdate}
|
||||
className="w-[9.5rem] justify-center transition-none"
|
||||
>
|
||||
<RefreshCw className="h-3.5 w-3.5" />
|
||||
{t("checkUpdate")}
|
||||
</Button>
|
||||
)}
|
||||
|
||||
{availableUpdate && (
|
||||
<Button
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -112,7 +112,14 @@
|
||||
"alreadyLatest": "当前已经是最新版本",
|
||||
"checkUpdateFailed": "检查更新失败:{message}",
|
||||
"installSuccess": "升级包已安装,正在重启应用",
|
||||
"installFailed": "升级失败:{message}"
|
||||
"installFailed": "升级失败:{message}",
|
||||
"updateErrors": {
|
||||
"sourceUnavailable": "无法连接更新源,请检查网络或代理设置后重试。",
|
||||
"network": "网络连接异常,请检查网络或代理设置后重试。",
|
||||
"downloadFailed": "下载更新包失败,请稍后重试。",
|
||||
"installFailed": "安装更新失败,请关闭应用后重试。",
|
||||
"unknown": "更新失败,请稍后重试。"
|
||||
}
|
||||
},
|
||||
"ShortcutSettings": {
|
||||
"sectionTitle": "快捷键",
|
||||
|
||||
@@ -112,7 +112,14 @@
|
||||
"alreadyLatest": "目前已是最新版本",
|
||||
"checkUpdateFailed": "檢查更新失敗:{message}",
|
||||
"installSuccess": "升級包已安裝,正在重新啟動應用",
|
||||
"installFailed": "升級失敗:{message}"
|
||||
"installFailed": "升級失敗:{message}",
|
||||
"updateErrors": {
|
||||
"sourceUnavailable": "無法連線更新來源,請檢查網路或代理設定後重試。",
|
||||
"network": "網路連線異常,請檢查網路或代理設定後重試。",
|
||||
"downloadFailed": "下載更新包失敗,請稍後再試。",
|
||||
"installFailed": "安裝更新失敗,請關閉應用後重試。",
|
||||
"unknown": "更新失敗,請稍後再試。"
|
||||
}
|
||||
},
|
||||
"ShortcutSettings": {
|
||||
"sectionTitle": "快捷鍵",
|
||||
|
||||
@@ -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 }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user