Files
OneCAD-Draft/src/update_check.rs
T
Hakan SevenandClaude Opus 4.7 86f5ba7b62 feat: in-app update notice — checks GitHub releases on startup
Adds a one-shot update check that runs on app boot and pops up a small
"Update Available" window when the GitHub releases API reports a newer
tag than this build's `CARGO_PKG_VERSION`.

- `src/update_check.rs` — async wrapper around a blocking `ureq` GET to
  `api.github.com/repos/HakanSeven12/H7CAD/releases/latest`, returns
  `Some(version)` when a newer release exists, `None` otherwise (silent
  on network / parse failure so a missing connection never blocks
  launch).
- `H7CAD::boot` chains a `Task::perform` next to the main-window open
  so the check runs in parallel with first-paint.
- New window plumbed like the other floating panels (page setup, about,
  etc.): `update_notice_window`/`update_notice_version` state, title
  entry, `OsWindowClosed` cleanup, and a `ui::update_notice::view_window`
  modal that shows installed vs. latest version plus "Later" /
  "Open Release Page" buttons. Open routes through `open::that` to the
  releases URL and dismisses the modal.

Dependency: adds `ureq = { version = "2", default-features = false,
features = ["tls"] }` — the smallest blocking HTTP client + TLS combo
that doesn't drag in an async runtime.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-16 19:55:57 +03:00

51 lines
1.7 KiB
Rust

// One-shot update check.
//
// `check_for_update()` runs on a background thread (joined inside an
// async wrapper, mirroring how the rest of `crate::io` wraps blocking
// work for iced's `Task::perform`). It hits the GitHub releases API and
// returns `Some(latest_version)` when a newer release is available, or
// `None` when up to date / on network failure / on parse error.
const RELEASES_API: &str =
"https://api.github.com/repos/HakanSeven12/H7CAD/releases/latest";
pub const RELEASES_PAGE: &str =
"https://github.com/HakanSeven12/H7CAD/releases/latest";
pub async fn check_for_update() -> Option<String> {
std::thread::spawn(fetch_latest_if_outdated)
.join()
.ok()
.flatten()
}
fn fetch_latest_if_outdated() -> Option<String> {
let body = ureq::AgentBuilder::new()
.timeout(std::time::Duration::from_secs(5))
.build()
.get(RELEASES_API)
.set("User-Agent", concat!("h7cad/", env!("CARGO_PKG_VERSION")))
.set("Accept", "application/vnd.github+json")
.call()
.ok()?
.into_string()
.ok()?;
let latest = extract_tag(&body)?
.trim_start_matches('v')
.to_string();
if latest != env!("CARGO_PKG_VERSION") {
Some(latest)
} else {
None
}
}
fn extract_tag(body: &str) -> Option<String> {
// Minimal substring extraction so we don't pull serde_json just for
// one field. The GitHub releases endpoint always returns
// `"tag_name":"v…"` near the top of the JSON object.
const KEY: &str = "\"tag_name\":\"";
let start = body.find(KEY)? + KEY.len();
let end = body[start..].find('"')? + start;
Some(body[start..end].to_string())
}