|
1 | 1 | import { useEffect, useState } from "react"; |
2 | 2 | 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 | +} |
3 | 18 |
|
4 | 19 | export function SettingsAboutSection() { |
5 | 20 | const [appBuildType, setAppBuildType] = useState<AppBuildType | "unknown">("unknown"); |
| 21 | + const { state: updaterState, checkForUpdates, startUpdate } = useUpdater({ |
| 22 | + enabled: false, |
| 23 | + }); |
6 | 24 |
|
7 | 25 | useEffect(() => { |
8 | 26 | let active = true; |
@@ -49,6 +67,69 @@ export function SettingsAboutSection() { |
49 | 67 | Build date: <code>{buildDateLabel}</code> |
50 | 68 | </div> |
51 | 69 | </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> |
52 | 133 | </section> |
53 | 134 | ); |
54 | 135 | } |
0 commit comments