Skip to content

Commit f4b8e51

Browse files
committed
feat(settings): add About update controls with download progress
1 parent 7746e5a commit f4b8e51

2 files changed

Lines changed: 85 additions & 1 deletion

File tree

src/App.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2132,7 +2132,10 @@ function MainApp() {
21322132
onDeleteWorktree: handleSidebarDeleteWorktree,
21332133
onLoadOlderThreads: handleSidebarLoadOlderThreads,
21342134
onReloadWorkspaceThreads: handleSidebarReloadWorkspaceThreads,
2135-
updaterState,
2135+
updaterState:
2136+
settingsOpen && settingsSection === "about"
2137+
? { stage: "idle" as const }
2138+
: updaterState,
21362139
onUpdate: startUpdate,
21372140
onDismissUpdate: dismissUpdate,
21382141
postUpdateNotice,

src/features/settings/components/sections/SettingsAboutSection.tsx

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,26 @@
11
import { useEffect, useState } from "react";
22
import { getAppBuildType, type AppBuildType } from "@services/tauri";
3+
import { useUpdater } from "@/features/update/hooks/useUpdater";
4+
5+
function formatBytes(value: number) {
6+
if (!Number.isFinite(value) || value <= 0) {
7+
return "0 B";
8+
}
9+
const units = ["B", "KB", "MB", "GB"];
10+
let size = value;
11+
let unitIndex = 0;
12+
while (size >= 1024 && unitIndex < units.length - 1) {
13+
size /= 1024;
14+
unitIndex += 1;
15+
}
16+
return `${size.toFixed(size >= 10 ? 0 : 1)} ${units[unitIndex]}`;
17+
}
318

419
export function SettingsAboutSection() {
520
const [appBuildType, setAppBuildType] = useState<AppBuildType | "unknown">("unknown");
21+
const { state: updaterState, checkForUpdates, startUpdate } = useUpdater({
22+
enabled: false,
23+
});
624

725
useEffect(() => {
826
let active = true;
@@ -49,6 +67,69 @@ export function SettingsAboutSection() {
4967
Build date: <code>{buildDateLabel}</code>
5068
</div>
5169
</div>
70+
<div className="settings-field">
71+
<div className="settings-label">App Updates</div>
72+
<div className="settings-help">
73+
Currently running version <code>{__APP_VERSION__}</code>
74+
</div>
75+
76+
{updaterState.stage === "error" && (
77+
<div className="settings-help ds-text-danger">
78+
Update failed: {updaterState.error}
79+
</div>
80+
)}
81+
82+
{updaterState.stage === "downloading" ||
83+
updaterState.stage === "installing" ||
84+
updaterState.stage === "restarting" ? (
85+
<div className="settings-help">
86+
{updaterState.stage === "downloading" ? (
87+
<>
88+
Downloading update...{" "}
89+
{updaterState.progress?.totalBytes
90+
? `${Math.round((updaterState.progress.downloadedBytes / updaterState.progress.totalBytes) * 100)}%`
91+
: formatBytes(updaterState.progress?.downloadedBytes ?? 0)}
92+
</>
93+
) : updaterState.stage === "installing" ? (
94+
"Installing update..."
95+
) : (
96+
"Restarting..."
97+
)}
98+
</div>
99+
) : updaterState.stage === "available" ? (
100+
<div className="settings-help">
101+
Version <code>{updaterState.version}</code> is available.
102+
</div>
103+
) : updaterState.stage === "latest" ? (
104+
<div className="settings-help">You are on the latest version.</div>
105+
) : null}
106+
107+
<div className="settings-controls">
108+
{updaterState.stage === "available" ? (
109+
<button
110+
type="button"
111+
className="primary"
112+
onClick={() => void startUpdate()}
113+
>
114+
Download & Install
115+
</button>
116+
) : (
117+
<button
118+
type="button"
119+
className="ghost"
120+
disabled={
121+
updaterState.stage === "checking" ||
122+
updaterState.stage === "downloading" ||
123+
updaterState.stage === "installing" ||
124+
updaterState.stage === "restarting"
125+
}
126+
onClick={() => void checkForUpdates({ announceNoUpdate: true })}
127+
>
128+
{updaterState.stage === "checking" ? "Checking..." : "Check for updates"}
129+
</button>
130+
)}
131+
</div>
132+
</div>
52133
</section>
53134
);
54135
}

0 commit comments

Comments
 (0)