mirror of
https://github.com/storytold/gridcraft.git
synced 2026-10-27 20:26:48 -06:00
feat: startup update prompt modal and direct self-updating (#2566)
* feat: startup update prompt modal and direct self-updating The update flow (check at start or on request, the startup prompt, the self-update worker) is craft-update's Updater; the app only supplies the suite, its close path and its status message. An install holds back while the workbook is unsaved. Ref: storytold/craft-libs#319 * build: refresh the lock * build: pin craft-update to craft-libs main (db19c37) * fix(updates): address review feedback Wake the UI when a manual check is queued from a menu callback; the update prompt owns the keyboard while it is open (LightCraft); Download opens through the desktop URL hook (FilmCraft); the Help menu dispatches through menus::invoke (FigCraft); print jobs and comment drafts hold an install back (WordCraft); Check for Updates is a registered command (GridCraft); an install holds back while documents are unsaved. * fix(updates): add Check for Updates… to the native macOS Help menu
This commit is contained in:
Generated
+209
-16
@@ -898,6 +898,31 @@ dependencies = [
|
||||
"egui",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "craft-update"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/storytold/craft-libs?rev=db19c373c8d59a545972ca340987aecfb3611a4b#db19c373c8d59a545972ca340987aecfb3611a4b"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"dunce",
|
||||
"egui",
|
||||
"flate2",
|
||||
"getrandom 0.3.4",
|
||||
"log",
|
||||
"plist",
|
||||
"semver",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha2 0.10.9",
|
||||
"tar",
|
||||
"tempfile",
|
||||
"ureq",
|
||||
"verifysign",
|
||||
"winreg",
|
||||
"winsafe",
|
||||
"zip 9.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "craft-winprint-gdi"
|
||||
version = "0.1.0"
|
||||
@@ -1016,6 +1041,12 @@ dependencies = [
|
||||
"syn 2.0.119",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "deranged"
|
||||
version = "0.5.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9e9de72ce2ad1f90dc62fa25f0f430ef85eb4b0d8fa0be4f30373bc40a21d28e"
|
||||
|
||||
[[package]]
|
||||
name = "derive_arbitrary"
|
||||
version = "1.5.0"
|
||||
@@ -1364,7 +1395,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.61.2",
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1838,6 +1869,7 @@ dependencies = [
|
||||
"arboard",
|
||||
"chrono",
|
||||
"craft-print",
|
||||
"craft-update",
|
||||
"eframe",
|
||||
"egui",
|
||||
"fmv-macos-events",
|
||||
@@ -1884,9 +1916,9 @@ dependencies = [
|
||||
"gridcraft-engine",
|
||||
"gridcraft-mcp",
|
||||
"log",
|
||||
"quick-xml",
|
||||
"quick-xml 0.41.0",
|
||||
"serde_json",
|
||||
"zip",
|
||||
"zip 3.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1918,7 +1950,7 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror 2.0.21",
|
||||
"zip",
|
||||
"zip 3.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1975,6 +2007,7 @@ name = "gridcraft-ui-egui"
|
||||
version = "0.12.1"
|
||||
dependencies = [
|
||||
"craft-ui",
|
||||
"craft-update",
|
||||
"egui",
|
||||
"egui_kittest",
|
||||
"epaint_default_fonts",
|
||||
@@ -2017,11 +2050,11 @@ dependencies = [
|
||||
"gridcraft-core",
|
||||
"gridcraft-formula",
|
||||
"gridcraft-model",
|
||||
"quick-xml",
|
||||
"quick-xml 0.41.0",
|
||||
"sha1 0.11.0",
|
||||
"sha2 0.11.0",
|
||||
"thiserror 2.0.21",
|
||||
"zip",
|
||||
"zip 3.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2741,7 +2774,7 @@ dependencies = [
|
||||
"objc2-foundation 0.3.2",
|
||||
"png",
|
||||
"thiserror 2.0.21",
|
||||
"windows-sys 0.61.2",
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2818,6 +2851,18 @@ dependencies = [
|
||||
"jni-sys 0.3.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.31.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf20d2fde8ff38632c426f1165ed7436270b44f199fc55284c38276f9db47c3d"
|
||||
dependencies = [
|
||||
"bitflags 2.13.2",
|
||||
"cfg-if",
|
||||
"cfg_aliases",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nohash-hasher"
|
||||
version = "0.2.0"
|
||||
@@ -2833,6 +2878,12 @@ dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-conv"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441"
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.19"
|
||||
@@ -3234,7 +3285,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7d8fae84b431384b68627d0f9b3b1245fcf9f46f6c0e3dc902e9dce64edd1967"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.61.2",
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3407,6 +3458,19 @@ version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6"
|
||||
|
||||
[[package]]
|
||||
name = "plist"
|
||||
version = "1.10.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "327a4cb2ad49dc48adfdc9da89e22c64d488b508c67a044d71ba729e4521a285"
|
||||
dependencies = [
|
||||
"base64 0.23.1",
|
||||
"indexmap",
|
||||
"quick-xml 0.42.0",
|
||||
"serde",
|
||||
"time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "png"
|
||||
version = "0.18.1"
|
||||
@@ -3479,6 +3543,12 @@ dependencies = [
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "powerfmt"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4a6394b9e965e73d0a289ee54f589087e2c676aedf60885baf52c76b771e4958"
|
||||
|
||||
[[package]]
|
||||
name = "presser"
|
||||
version = "0.3.1"
|
||||
@@ -3554,6 +3624,15 @@ dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quick-xml"
|
||||
version = "0.42.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41b1177fdf999d2321d3fb46ff47159d9c1fb9ad66a4879f8c50a0b504615e9b"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quinn"
|
||||
version = "0.11.12"
|
||||
@@ -3608,7 +3687,7 @@ dependencies = [
|
||||
"once_cell",
|
||||
"socket2",
|
||||
"tracing",
|
||||
"windows-sys 0.61.2",
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3915,7 +3994,7 @@ dependencies = [
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys 0.12.1",
|
||||
"windows-sys 0.61.2",
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3925,7 +4004,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "48e13bd8c0e9365c43cfa5c9e8f9ad49d3c8444926c9aac819e0e4dc503c8fdf"
|
||||
dependencies = [
|
||||
"aws-lc-rs",
|
||||
"log",
|
||||
"once_cell",
|
||||
"ring",
|
||||
"rustls-pki-types",
|
||||
"rustls-webpki",
|
||||
"subtle",
|
||||
@@ -3972,7 +4053,7 @@ dependencies = [
|
||||
"security-framework",
|
||||
"security-framework-sys",
|
||||
"webpki-root-certs",
|
||||
"windows-sys 0.61.2",
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4075,6 +4156,10 @@ name = "semver"
|
||||
version = "1.0.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "send_wrapper"
|
||||
@@ -4535,7 +4620,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.61.2",
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4699,7 +4784,7 @@ dependencies = [
|
||||
"getrandom 0.4.3",
|
||||
"once_cell",
|
||||
"rustix 1.1.5",
|
||||
"windows-sys 0.61.2",
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4765,6 +4850,36 @@ dependencies = [
|
||||
"zune-jpeg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.3.55"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cdb87b95ec50ddfa440816d227a17b2ccbdda963a316a727fda0fc4334f7d134"
|
||||
dependencies = [
|
||||
"deranged",
|
||||
"num-conv",
|
||||
"powerfmt",
|
||||
"serde_core",
|
||||
"time-core",
|
||||
"time-macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time-core"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109"
|
||||
|
||||
[[package]]
|
||||
name = "time-macros"
|
||||
version = "0.2.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7e689342a48d2ea927c87ea50cabf8594854bf940e9310208848d680d668ed85"
|
||||
dependencies = [
|
||||
"num-conv",
|
||||
"time-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinystr"
|
||||
version = "0.8.4"
|
||||
@@ -4994,6 +5109,12 @@ dependencies = [
|
||||
"rustc-hash 2.1.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typed-path"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ce898823d8cd0f028662d2f349694b1af0a5cc9be83f1d213a00d3147445e1e"
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.20.1"
|
||||
@@ -5008,7 +5129,7 @@ checksum = "f2f6fb2847f6742cd76af783a2a2c49e9375d0a111c7bef6f71cd9e738c72d6e"
|
||||
dependencies = [
|
||||
"memoffset",
|
||||
"tempfile",
|
||||
"windows-sys 0.61.2",
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5059,6 +5180,35 @@ version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
|
||||
|
||||
[[package]]
|
||||
name = "ureq"
|
||||
version = "3.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cde93ff05758a36a851a398a772b0b79a82fbe3c186a46f2696baaeaf84c966b"
|
||||
dependencies = [
|
||||
"base64 0.23.1",
|
||||
"flate2",
|
||||
"log",
|
||||
"percent-encoding",
|
||||
"rustls",
|
||||
"rustls-pki-types",
|
||||
"rustls-platform-verifier",
|
||||
"ureq-proto",
|
||||
"utf8-zero",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ureq-proto"
|
||||
version = "0.6.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "841583fc36089d7d59419568ae157f43a5a03a2e5a81ba87c77d1596ef46e922"
|
||||
dependencies = [
|
||||
"base64 0.23.1",
|
||||
"http",
|
||||
"httparse",
|
||||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "url"
|
||||
version = "2.5.8"
|
||||
@@ -5071,6 +5221,12 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "utf8-zero"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8c0a043c9540bae7c578c88f91dda8bd82e59ae27c21baca69c8b191aaf5a6e"
|
||||
|
||||
[[package]]
|
||||
name = "utf8_iter"
|
||||
version = "1.0.4"
|
||||
@@ -5115,6 +5271,16 @@ dependencies = [
|
||||
"vello_common",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "verifysign"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6acfcc42a8d44f340d001193d97ef56bf33210b51ad760ae8308c97d5241ca51"
|
||||
dependencies = [
|
||||
"core-foundation 0.10.1",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.5"
|
||||
@@ -5330,7 +5496,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "338e30461b3a2b67d70eb30a6d89f8e0c93a833e07d2ae89085cd070c4a00ac0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quick-xml",
|
||||
"quick-xml 0.41.0",
|
||||
"quote",
|
||||
]
|
||||
|
||||
@@ -5570,7 +5736,7 @@ version = "0.1.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
|
||||
dependencies = [
|
||||
"windows-sys 0.61.2",
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5939,6 +6105,16 @@ dependencies = [
|
||||
"xml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winreg"
|
||||
version = "0.55.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb5a765337c50e9ec252c2069be9bf91c7df47afb103b642ba3a53bf8101be97"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winresource"
|
||||
version = "0.1.31"
|
||||
@@ -6334,6 +6510,23 @@ dependencies = [
|
||||
"zopfli",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zip"
|
||||
version = "9.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2886b00003a72c4ccee3f42cfc34908d33b2508a74d827dc2a4244706475c704"
|
||||
dependencies = [
|
||||
"crc32fast",
|
||||
"flate2",
|
||||
"getrandom 0.4.3",
|
||||
"indexmap",
|
||||
"memchr",
|
||||
"nix",
|
||||
"typed-path",
|
||||
"windows-sys 0.61.2",
|
||||
"zopfli",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zlib-rs"
|
||||
version = "0.6.8"
|
||||
|
||||
@@ -24,6 +24,7 @@ gridcraft-engine = { path = "crates/engine" }
|
||||
gridcraft-ui-egui = { path = "crates/ui-egui" }
|
||||
gridcraft-mcp = { path = "crates/mcp" }
|
||||
gridcraft-pdf = { path = "crates/pdf" }
|
||||
craft-update = { git = "https://github.com/storytold/craft-libs", version = "=0.1.0", rev = "db19c373c8d59a545972ca340987aecfb3611a4b", features = ["egui"] }
|
||||
|
||||
# foundation
|
||||
thiserror = "2"
|
||||
|
||||
@@ -26,6 +26,7 @@ winresource = "0.1"
|
||||
|
||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||
craft-print = { git = "https://github.com/storytold/craft-libs", rev = "25da1314d443171b4adeaad2cd9454b273f35c6f" }
|
||||
craft-update = { workspace = true }
|
||||
rfd = { workspace = true }
|
||||
arboard = { workspace = true }
|
||||
|
||||
|
||||
@@ -251,6 +251,14 @@ fn main() -> eframe::Result<()> {
|
||||
}
|
||||
}));
|
||||
let args: Vec<String> = std::env::args().skip(1).collect();
|
||||
// The restart helper a self-update spawns: it finishes the swap and relaunches, then exits.
|
||||
if let Some(done) = craft_update::self_update::finish(&craft_update::suite::GRIDCRAFT, &std::env::args_os().skip(1).collect::<Vec<_>>()) {
|
||||
if let Err(e) = done {
|
||||
eprintln!("GridCraft update helper failed: {e}");
|
||||
std::process::exit(1);
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
if args.iter().any(|a| a == "--version" || a == "-V") {
|
||||
println!("GridCraft {}", env!("CARGO_PKG_VERSION"));
|
||||
return Ok(());
|
||||
@@ -299,6 +307,7 @@ fn main() -> eframe::Result<()> {
|
||||
}
|
||||
let mut app = SheetApp::new(session, services());
|
||||
app.watch_desktop_theme();
|
||||
app.start_update_check();
|
||||
app.after_engine(); // Show warnings from the sample.
|
||||
let control_port = control_port;
|
||||
load_prefs(&mut app);
|
||||
|
||||
@@ -172,6 +172,7 @@ fn tree() -> Vec<(&'static str, Vec<Entry>)> {
|
||||
(
|
||||
"Help",
|
||||
vec![
|
||||
("Check for Updates…", "app.checkForUpdates", None),
|
||||
("Agent Control (MCP)…", "dialog:agents", None),
|
||||
("Contributors…", "dialog:about:Contributors", None),
|
||||
("GridCraft on getartcraft.com", "url:https://getartcraft.com/apps/gridcraft", None),
|
||||
@@ -337,6 +338,16 @@ mod tests {
|
||||
assert!(gridcraft_engine::find_command("file.exportPdf").is_some());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn help_menu_checks_for_updates_through_the_registered_command() {
|
||||
let tree = tree();
|
||||
let (_, help) = tree.iter().find(|(title, _)| *title == "Help").expect("Help menu");
|
||||
let item = help.iter().find(|e| e.0 == "Check for Updates…").expect("Check for Updates… in the Help menu");
|
||||
assert_eq!(item.1, "app.checkForUpdates");
|
||||
let mut app = SheetApp::new(gridcraft_engine::Session::new(), Default::default());
|
||||
assert!(app.run(item.1, serde_json::json!({})).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn theme_choices_use_the_shared_preference_command() {
|
||||
let mut app = SheetApp::new(gridcraft_engine::Session::new(), Default::default());
|
||||
|
||||
@@ -36,6 +36,7 @@ epaint_default_fonts = { workspace = true }
|
||||
gridcraft-xlsx = { workspace = true, features = ["test-support"] }
|
||||
|
||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||
craft-update = { workspace = true }
|
||||
# The desktop's preferred language for the first-run interface language.
|
||||
sys-locale = { workspace = true }
|
||||
|
||||
|
||||
@@ -595,6 +595,7 @@ pub fn handle(app: &mut SheetApp, ctx: &egui::Context, method: &str, p: &Json) -
|
||||
let language_params = format!("{{language: {}}} (alias: code)", crate::i18n::Language::code_list("|"));
|
||||
for (id, label, params) in [
|
||||
("ui.ribbonTab", "Ribbon Tab", ""),
|
||||
("app.checkForUpdates", "Check for Updates…", ""),
|
||||
("view.collapseRibbon", "Collapse Ribbon", ""),
|
||||
("view.expandFormulaBar", "Expand Formula Bar", "{on?: bool} (toggles without it) → {on}"),
|
||||
("view.darkMode", "Dark Mode", ""),
|
||||
|
||||
@@ -35,6 +35,8 @@ pub mod selection_glide;
|
||||
pub mod tabs;
|
||||
pub mod text_box;
|
||||
pub mod theme;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
mod updates_ui;
|
||||
pub mod widgets;
|
||||
|
||||
use std::collections::HashMap;
|
||||
@@ -326,6 +328,9 @@ pub struct SheetApp {
|
||||
/// Every unsaved workbook has been answered for quitting (or `app.quit` asked): the next
|
||||
/// window close goes through.
|
||||
pub allow_close: bool,
|
||||
/// The update check, the startup prompt and a running self-update (desktop only).
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
updates_rt: updates_ui::Runtime,
|
||||
}
|
||||
|
||||
impl SheetApp {
|
||||
@@ -379,6 +384,8 @@ impl SheetApp {
|
||||
tab_run: None,
|
||||
started: now_ms(),
|
||||
allow_close: false,
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
updates_rt: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -666,6 +673,10 @@ impl SheetApp {
|
||||
/// UI-only commands (dialogs, view toggles that aren't saved in the file).
|
||||
fn run_ui_command(&mut self, id: &str, p: &Json) -> Option<Result<Json, String>> {
|
||||
let r = match id {
|
||||
"app.checkForUpdates" | "help.checkForUpdates" => {
|
||||
self.check_for_updates();
|
||||
Ok(Json::Null)
|
||||
}
|
||||
"ui.printSettings" => printing::settings(&mut self.printing, p, self.services.printers.as_ref()),
|
||||
"ui.printSubmit" => self.submit_print(p.clone()),
|
||||
"ui.ribbonTab" => {
|
||||
@@ -1234,6 +1245,8 @@ impl SheetApp {
|
||||
// frames still see worker completion. `ui` and the desktop app no longer poll it.
|
||||
self.poll_printing(ctx);
|
||||
self.guard_window_close(ctx);
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
updates_ui::tick(self, ctx);
|
||||
// Files read asynchronously (web file picker, dropped files).
|
||||
self.poll_async_opens();
|
||||
self.autosave(ctx);
|
||||
@@ -1395,6 +1408,23 @@ impl SheetApp {
|
||||
}
|
||||
}
|
||||
|
||||
/// Look for a newer release once, in the background, as the desktop app starts (a no-op on the
|
||||
/// web). A found release opens the update prompt; nothing is shown when GridCraft is current.
|
||||
pub fn start_update_check(&mut self) {
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
updates_ui::start(self, false);
|
||||
}
|
||||
|
||||
/// Help ▸ Check for Updates…: look now, and say so when GridCraft is up to date or the check fails.
|
||||
pub fn check_for_updates(&mut self) {
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
updates_ui::start(self, true);
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
{
|
||||
self.message = Some(("GridCraft".into(), "The web version is always the latest release.".into()));
|
||||
}
|
||||
}
|
||||
|
||||
/// Window title, next to the close/autosave state: `ViewportCommand::Title` is valid outside a
|
||||
/// pass and a control-channel rename while hidden updates it on return.
|
||||
fn update_title(&mut self, ctx: &egui::Context) {
|
||||
@@ -1473,6 +1503,8 @@ impl SheetApp {
|
||||
text_box::before_ui(self, &ctx);
|
||||
// The language the widgets translate with this frame (see `i18n::current`).
|
||||
i18n::set_current(&ctx, self.ui.language);
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
updates_ui::show(self, &ctx);
|
||||
let t = theme::Tokens::get(&ctx);
|
||||
// A host can turn the File tab off while Backstage is already open: close it.
|
||||
if !self.chrome.file_tab {
|
||||
|
||||
@@ -2614,6 +2614,11 @@ fn automate(app: &mut SheetApp, ui: &mut Ui) {
|
||||
if __k.clicked() {
|
||||
app.open_dialog("journal", json!({}));
|
||||
}
|
||||
let __k = big_button(ui, Icon::Script, "Check for\nUpdates…", "Look for a newer GridCraft release", false);
|
||||
control_keytip(app, ui, &__k, "checkForUpdates");
|
||||
if __k.clicked() {
|
||||
app.run_or_alert("app.checkForUpdates", json!({}));
|
||||
}
|
||||
let __k = big_button(ui, Icon::Script, "About\nGridCraft", "Version, contributors and the AI models that helped", false);
|
||||
control_keytip(app, ui, &__k, "about");
|
||||
if __k.clicked() {
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
//! One background check at startup (and on Help ▸ Check for Updates…) and the modal prompt that
|
||||
//! offers the new release: Update and Restart (direct self-update), Download or Later. The flow
|
||||
//! itself is `craft_update::updater::Updater`.
|
||||
|
||||
use craft_update::suite::GRIDCRAFT;
|
||||
use craft_update::updater::{Action, Status, Updater};
|
||||
|
||||
use crate::SheetApp;
|
||||
|
||||
const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
|
||||
pub(crate) struct Runtime {
|
||||
updater: Updater,
|
||||
/// The check at start was asked for; `tick` starts it with the context.
|
||||
startup: bool,
|
||||
/// Help ▸ Check for Updates… was chosen; the next `tick` starts it.
|
||||
manual: bool,
|
||||
}
|
||||
|
||||
impl Default for Runtime {
|
||||
fn default() -> Self {
|
||||
Self { updater: Updater::new(&GRIDCRAFT, VERSION), startup: false, manual: false }
|
||||
}
|
||||
}
|
||||
|
||||
/// Ask for a check; the next frame's `tick` starts it. A manual one reports every outcome.
|
||||
pub(crate) fn start(app: &mut SheetApp, manual: bool) {
|
||||
if manual {
|
||||
app.updates_rt.manual = true;
|
||||
} else {
|
||||
app.updates_rt.startup = true;
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn tick(app: &mut SheetApp, ctx: &egui::Context) {
|
||||
let rt = &mut app.updates_rt;
|
||||
if std::mem::take(&mut rt.manual) {
|
||||
rt.updater.check_now(ctx);
|
||||
} else if rt.startup {
|
||||
rt.updater.check_at_start(ctx);
|
||||
}
|
||||
rt.updater.tick(ctx);
|
||||
let lang = app.ui.language;
|
||||
let outcome = match rt.updater.status() {
|
||||
Status::UpToDate => Some(lang.tr("GridCraft {version} is up to date.").replace("{version}", VERSION)),
|
||||
Status::Failed(e) => Some(lang.tr("Could not check for updates: {error}").replace("{error}", e)),
|
||||
Status::Idle | Status::Checking => None,
|
||||
};
|
||||
if let Some(message) = outcome {
|
||||
rt.updater.dismiss_status();
|
||||
app.message = Some(("GridCraft".into(), message));
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn show(app: &mut SheetApp, ctx: &egui::Context) {
|
||||
// The restart helper is scheduled before the app closes, so no update starts over unsaved work.
|
||||
let clean = !app.session.documents().iter().any(|d| d.is_dirty());
|
||||
match app.updates_rt.updater.show_if(ctx, clean) {
|
||||
Action::OpenUrl(url) => {
|
||||
if let Some(open) = &app.services.open_url {
|
||||
open(&url);
|
||||
}
|
||||
}
|
||||
// The guarded close path asks about unsaved work; the restart helper relaunches once the
|
||||
// process has exited.
|
||||
Action::Close => ctx.send_viewport_cmd(egui::ViewportCommand::Close),
|
||||
Action::None => {}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user