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 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
70 lines
2.4 KiB
Rust
70 lines
2.4 KiB
Rust
//! 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 => {}
|
|
}
|
|
}
|