feat: show real-time download progress bar for app updates
Leverage the Tauri updater plugin's DownloadEvent callback to display a progress bar with downloaded/total bytes during app updates, replacing the previous spinner-only feedback. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -39,8 +39,15 @@ import {
|
|||||||
normalizeAppUpdateError,
|
normalizeAppUpdateError,
|
||||||
relaunchApp,
|
relaunchApp,
|
||||||
} from "@/lib/updater"
|
} from "@/lib/updater"
|
||||||
|
import type { DownloadEvent } from "@/lib/updater"
|
||||||
import { APP_LOCALES } from "@/lib/i18n"
|
import { APP_LOCALES } from "@/lib/i18n"
|
||||||
|
|
||||||
|
function formatBytes(bytes: number): string {
|
||||||
|
if (bytes < 1024) return `${bytes} B`
|
||||||
|
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`
|
||||||
|
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`
|
||||||
|
}
|
||||||
|
|
||||||
const PROXY_EXAMPLE = "http://127.0.0.1:7890"
|
const PROXY_EXAMPLE = "http://127.0.0.1:7890"
|
||||||
const APP_LANGUAGE_VALUES = APP_LOCALES
|
const APP_LANGUAGE_VALUES = APP_LOCALES
|
||||||
|
|
||||||
@@ -71,6 +78,11 @@ export function SystemNetworkSettings() {
|
|||||||
const [installingUpdate, setInstallingUpdate] = useState(false)
|
const [installingUpdate, setInstallingUpdate] = useState(false)
|
||||||
const [updateError, setUpdateError] = useState<string | null>(null)
|
const [updateError, setUpdateError] = useState<string | null>(null)
|
||||||
const [lastCheckedAt, setLastCheckedAt] = useState<Date | null>(null)
|
const [lastCheckedAt, setLastCheckedAt] = useState<Date | null>(null)
|
||||||
|
const [downloadProgress, setDownloadProgress] = useState<{
|
||||||
|
downloaded: number
|
||||||
|
total: number | null
|
||||||
|
phase: "downloading" | "installing"
|
||||||
|
} | null>(null)
|
||||||
|
|
||||||
const [appLanguage, setAppLanguage] = useState<LanguageSelectValue>(
|
const [appLanguage, setAppLanguage] = useState<LanguageSelectValue>(
|
||||||
languageSettings.mode === "system" ? "system" : languageSettings.language
|
languageSettings.mode === "system" ? "system" : languageSettings.language
|
||||||
@@ -273,9 +285,37 @@ export function SystemNetworkSettings() {
|
|||||||
|
|
||||||
setInstallingUpdate(true)
|
setInstallingUpdate(true)
|
||||||
setUpdateError(null)
|
setUpdateError(null)
|
||||||
|
setDownloadProgress(null)
|
||||||
|
|
||||||
|
let downloaded = 0
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await installAppUpdate(availableUpdate)
|
await installAppUpdate(availableUpdate, (event: DownloadEvent) => {
|
||||||
|
switch (event.event) {
|
||||||
|
case "Started":
|
||||||
|
setDownloadProgress({
|
||||||
|
downloaded: 0,
|
||||||
|
total: event.data.contentLength ?? null,
|
||||||
|
phase: "downloading",
|
||||||
|
})
|
||||||
|
break
|
||||||
|
case "Progress":
|
||||||
|
downloaded += event.data.chunkLength
|
||||||
|
setDownloadProgress((prev) => ({
|
||||||
|
downloaded,
|
||||||
|
total: prev?.total ?? null,
|
||||||
|
phase: "downloading",
|
||||||
|
}))
|
||||||
|
break
|
||||||
|
case "Finished":
|
||||||
|
setDownloadProgress((prev) => ({
|
||||||
|
downloaded: prev?.downloaded ?? downloaded,
|
||||||
|
total: prev?.total ?? null,
|
||||||
|
phase: "installing",
|
||||||
|
}))
|
||||||
|
break
|
||||||
|
}
|
||||||
|
})
|
||||||
toast.success(t("installSuccess"))
|
toast.success(t("installSuccess"))
|
||||||
await relaunchApp()
|
await relaunchApp()
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -285,6 +325,7 @@ export function SystemNetworkSettings() {
|
|||||||
console.error("[Settings] install app update failed:", err)
|
console.error("[Settings] install app update failed:", err)
|
||||||
} finally {
|
} finally {
|
||||||
setInstallingUpdate(false)
|
setInstallingUpdate(false)
|
||||||
|
setDownloadProgress(null)
|
||||||
}
|
}
|
||||||
}, [availableUpdate, formatUpdateError, t])
|
}, [availableUpdate, formatUpdateError, t])
|
||||||
|
|
||||||
@@ -389,10 +430,39 @@ export function SystemNetworkSettings() {
|
|||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{updateStatusMessage && (
|
{updateStatusMessage && !downloadProgress && (
|
||||||
<p className="text-muted-foreground">{updateStatusMessage}</p>
|
<p className="text-muted-foreground">{updateStatusMessage}</p>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{downloadProgress && (
|
||||||
|
<div className="space-y-1.5">
|
||||||
|
<div className="flex items-center justify-between text-muted-foreground">
|
||||||
|
<span>
|
||||||
|
{downloadProgress.phase === "downloading"
|
||||||
|
? t("downloading")
|
||||||
|
: t("updating")}
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
{formatBytes(downloadProgress.downloaded)}
|
||||||
|
{downloadProgress.total
|
||||||
|
? ` / ${formatBytes(downloadProgress.total)}`
|
||||||
|
: ""}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="h-1.5 rounded-full bg-muted overflow-hidden">
|
||||||
|
<div
|
||||||
|
className="h-full rounded-full bg-primary transition-all duration-300"
|
||||||
|
style={{
|
||||||
|
width:
|
||||||
|
downloadProgress.total && downloadProgress.total > 0
|
||||||
|
? `${Math.min(100, (downloadProgress.downloaded / downloadProgress.total) * 100)}%`
|
||||||
|
: "30%",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{availableUpdate && (
|
{availableUpdate && (
|
||||||
<div className="space-y-2 pt-2 border-t border-border/70">
|
<div className="space-y-2 pt-2 border-t border-border/70">
|
||||||
<div className="flex items-center justify-between gap-3">
|
<div className="flex items-center justify-between gap-3">
|
||||||
|
|||||||
@@ -172,6 +172,7 @@
|
|||||||
"checking": "جارٍ التحقق...",
|
"checking": "جارٍ التحقق...",
|
||||||
"checkUpdate": "التحقق من التحديثات",
|
"checkUpdate": "التحقق من التحديثات",
|
||||||
"updating": "جارٍ التثبيت...",
|
"updating": "جارٍ التثبيت...",
|
||||||
|
"downloading": "جارٍ التنزيل...",
|
||||||
"upgradeTo": "الترقية إلى v{version}",
|
"upgradeTo": "الترقية إلى v{version}",
|
||||||
"foundUpdate": "تم العثور على إصدار جديد v{version}",
|
"foundUpdate": "تم العثور على إصدار جديد v{version}",
|
||||||
"alreadyLatest": "أنت على أحدث إصدار",
|
"alreadyLatest": "أنت على أحدث إصدار",
|
||||||
|
|||||||
@@ -172,6 +172,7 @@
|
|||||||
"checking": "Wird geprüft...",
|
"checking": "Wird geprüft...",
|
||||||
"checkUpdate": "Nach Updates suchen",
|
"checkUpdate": "Nach Updates suchen",
|
||||||
"updating": "Wird installiert...",
|
"updating": "Wird installiert...",
|
||||||
|
"downloading": "Herunterladen...",
|
||||||
"upgradeTo": "Auf v{version} aktualisieren",
|
"upgradeTo": "Auf v{version} aktualisieren",
|
||||||
"foundUpdate": "Neue Version v{version} gefunden",
|
"foundUpdate": "Neue Version v{version} gefunden",
|
||||||
"alreadyLatest": "Du verwendest bereits die neueste Version",
|
"alreadyLatest": "Du verwendest bereits die neueste Version",
|
||||||
|
|||||||
@@ -172,6 +172,7 @@
|
|||||||
"checking": "Checking...",
|
"checking": "Checking...",
|
||||||
"checkUpdate": "Check for updates",
|
"checkUpdate": "Check for updates",
|
||||||
"updating": "Installing...",
|
"updating": "Installing...",
|
||||||
|
"downloading": "Downloading...",
|
||||||
"upgradeTo": "Upgrade to v{version}",
|
"upgradeTo": "Upgrade to v{version}",
|
||||||
"foundUpdate": "New version v{version} found",
|
"foundUpdate": "New version v{version} found",
|
||||||
"alreadyLatest": "You're on the latest version",
|
"alreadyLatest": "You're on the latest version",
|
||||||
|
|||||||
@@ -172,6 +172,7 @@
|
|||||||
"checking": "Comprobando...",
|
"checking": "Comprobando...",
|
||||||
"checkUpdate": "Buscar actualizaciones",
|
"checkUpdate": "Buscar actualizaciones",
|
||||||
"updating": "Instalando...",
|
"updating": "Instalando...",
|
||||||
|
"downloading": "Descargando...",
|
||||||
"upgradeTo": "Actualizar a v{version}",
|
"upgradeTo": "Actualizar a v{version}",
|
||||||
"foundUpdate": "Nueva versión v{version} encontrada",
|
"foundUpdate": "Nueva versión v{version} encontrada",
|
||||||
"alreadyLatest": "Ya tienes la versión más reciente",
|
"alreadyLatest": "Ya tienes la versión más reciente",
|
||||||
|
|||||||
@@ -172,6 +172,7 @@
|
|||||||
"checking": "Vérification...",
|
"checking": "Vérification...",
|
||||||
"checkUpdate": "Rechercher les mises à jour",
|
"checkUpdate": "Rechercher les mises à jour",
|
||||||
"updating": "Installation...",
|
"updating": "Installation...",
|
||||||
|
"downloading": "Téléchargement...",
|
||||||
"upgradeTo": "Mettre à jour vers v{version}",
|
"upgradeTo": "Mettre à jour vers v{version}",
|
||||||
"foundUpdate": "Nouvelle version v{version} trouvée",
|
"foundUpdate": "Nouvelle version v{version} trouvée",
|
||||||
"alreadyLatest": "Vous utilisez déjà la dernière version",
|
"alreadyLatest": "Vous utilisez déjà la dernière version",
|
||||||
|
|||||||
@@ -172,6 +172,7 @@
|
|||||||
"checking": "確認中...",
|
"checking": "確認中...",
|
||||||
"checkUpdate": "更新を確認",
|
"checkUpdate": "更新を確認",
|
||||||
"updating": "インストール中...",
|
"updating": "インストール中...",
|
||||||
|
"downloading": "ダウンロード中...",
|
||||||
"upgradeTo": "v{version} にアップグレード",
|
"upgradeTo": "v{version} にアップグレード",
|
||||||
"foundUpdate": "新しいバージョン v{version} が見つかりました",
|
"foundUpdate": "新しいバージョン v{version} が見つかりました",
|
||||||
"alreadyLatest": "すでに最新バージョンです",
|
"alreadyLatest": "すでに最新バージョンです",
|
||||||
|
|||||||
@@ -172,6 +172,7 @@
|
|||||||
"checking": "확인 중...",
|
"checking": "확인 중...",
|
||||||
"checkUpdate": "업데이트 확인",
|
"checkUpdate": "업데이트 확인",
|
||||||
"updating": "설치 중...",
|
"updating": "설치 중...",
|
||||||
|
"downloading": "다운로드 중...",
|
||||||
"upgradeTo": "v{version}로 업그레이드",
|
"upgradeTo": "v{version}로 업그레이드",
|
||||||
"foundUpdate": "새 버전 v{version}을 찾았습니다",
|
"foundUpdate": "새 버전 v{version}을 찾았습니다",
|
||||||
"alreadyLatest": "이미 최신 버전입니다",
|
"alreadyLatest": "이미 최신 버전입니다",
|
||||||
|
|||||||
@@ -172,6 +172,7 @@
|
|||||||
"checking": "Verificando...",
|
"checking": "Verificando...",
|
||||||
"checkUpdate": "Verificar atualizações",
|
"checkUpdate": "Verificar atualizações",
|
||||||
"updating": "Instalando...",
|
"updating": "Instalando...",
|
||||||
|
"downloading": "Baixando...",
|
||||||
"upgradeTo": "Atualizar para v{version}",
|
"upgradeTo": "Atualizar para v{version}",
|
||||||
"foundUpdate": "Nova versão v{version} encontrada",
|
"foundUpdate": "Nova versão v{version} encontrada",
|
||||||
"alreadyLatest": "Você já está na versão mais recente",
|
"alreadyLatest": "Você já está na versão mais recente",
|
||||||
|
|||||||
@@ -172,6 +172,7 @@
|
|||||||
"checking": "检查中...",
|
"checking": "检查中...",
|
||||||
"checkUpdate": "检查更新",
|
"checkUpdate": "检查更新",
|
||||||
"updating": "升级中...",
|
"updating": "升级中...",
|
||||||
|
"downloading": "下载中...",
|
||||||
"upgradeTo": "升级到 v{version}",
|
"upgradeTo": "升级到 v{version}",
|
||||||
"foundUpdate": "发现新版本 v{version}",
|
"foundUpdate": "发现新版本 v{version}",
|
||||||
"alreadyLatest": "当前已经是最新版本",
|
"alreadyLatest": "当前已经是最新版本",
|
||||||
|
|||||||
@@ -172,6 +172,7 @@
|
|||||||
"checking": "檢查中...",
|
"checking": "檢查中...",
|
||||||
"checkUpdate": "檢查更新",
|
"checkUpdate": "檢查更新",
|
||||||
"updating": "升級中...",
|
"updating": "升級中...",
|
||||||
|
"downloading": "下載中...",
|
||||||
"upgradeTo": "升級到 v{version}",
|
"upgradeTo": "升級到 v{version}",
|
||||||
"foundUpdate": "發現新版本 v{version}",
|
"foundUpdate": "發現新版本 v{version}",
|
||||||
"alreadyLatest": "目前已是最新版本",
|
"alreadyLatest": "目前已是最新版本",
|
||||||
|
|||||||
@@ -4,6 +4,11 @@ import { getTransport, isDesktop } from "./transport"
|
|||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
type Update = any
|
type Update = any
|
||||||
|
|
||||||
|
export type DownloadEvent =
|
||||||
|
| { event: "Started"; data: { contentLength?: number } }
|
||||||
|
| { event: "Progress"; data: { chunkLength: number } }
|
||||||
|
| { event: "Finished" }
|
||||||
|
|
||||||
export interface AppUpdateCheckResult {
|
export interface AppUpdateCheckResult {
|
||||||
currentVersion: string
|
currentVersion: string
|
||||||
update: Update | null
|
update: Update | null
|
||||||
@@ -46,9 +51,10 @@ export async function checkAppUpdate(): Promise<AppUpdateCheckResult> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function installAppUpdate(
|
export async function installAppUpdate(
|
||||||
update: NonNullable<Update>
|
update: NonNullable<Update>,
|
||||||
|
onEvent?: (progress: DownloadEvent) => void
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
await update.downloadAndInstall()
|
await update.downloadAndInstall(onEvent)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function relaunchApp(): Promise<void> {
|
export async function relaunchApp(): Promise<void> {
|
||||||
|
|||||||
Reference in New Issue
Block a user