Skip to content

Commit

Permalink
feat(core): use AppHandle instead of Window on the updater logic (#3702)
Browse files Browse the repository at this point in the history
  • Loading branch information
lucasfernog authored Mar 15, 2022
1 parent 5d538ec commit c4ca80f
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 77 deletions.
5 changes: 5 additions & 0 deletions .changes/updater-no-window.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"tauri": patch
---

Run the updater on startup even if no window was created.
81 changes: 37 additions & 44 deletions core/tauri/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -567,54 +567,54 @@ impl<R: Runtime> App<R> {
#[cfg(feature = "updater")]
impl<R: Runtime> App<R> {
/// Runs the updater hook with built-in dialog.
fn run_updater_dialog(&self, window: Window<R>) {
fn run_updater_dialog(&self) {
let updater_config = self.manager.config().tauri.updater.clone();
let package_info = self.manager.package_info().clone();
let handle = self.handle();

crate::async_runtime::spawn(async move {
updater::check_update_with_dialog(updater_config, package_info, window).await
updater::check_update_with_dialog(updater_config, package_info, handle).await
});
}

/// Listen updater events when dialog are disabled.
fn listen_updater_events(&self, window: Window<R>) {
fn listen_updater_events(&self, handle: AppHandle<R>) {
let updater_config = self.manager.config().tauri.updater.clone();
updater::listener(updater_config, self.manager.package_info().clone(), &window);
updater::listener(updater_config, self.manager.package_info().clone(), &handle);
}

fn run_updater(&self, main_window: Option<Window<R>>) {
if let Some(main_window) = main_window {
let event_window = main_window.clone();
let updater_config = self.manager.config().tauri.updater.clone();
// check if updater is active or not
if updater_config.dialog && updater_config.active {
// if updater dialog is enabled spawn a new task
self.run_updater_dialog(main_window.clone());
let config = self.manager.config().tauri.updater.clone();
let package_info = self.manager.package_info().clone();
// When dialog is enabled, if user want to recheck
// if an update is available after first start
// invoke the Event `tauri://update` from JS or rust side.
main_window.listen(updater::EVENT_CHECK_UPDATE, move |_msg| {
let window = event_window.clone();
let package_info = package_info.clone();
let config = config.clone();
// re-spawn task inside tokyo to launch the download
// we don't need to emit anything as everything is handled
// by the process (user is asked to restart at the end)
// and it's handled by the updater
crate::async_runtime::spawn(async move {
updater::check_update_with_dialog(config, package_info, window).await
});
fn run_updater(&self) {
let handle = self.handle();
let handle_ = handle.clone();
let updater_config = self.manager.config().tauri.updater.clone();
// check if updater is active or not
if updater_config.dialog && updater_config.active {
// if updater dialog is enabled spawn a new task
self.run_updater_dialog();
let config = self.manager.config().tauri.updater.clone();
let package_info = self.manager.package_info().clone();
// When dialog is enabled, if user want to recheck
// if an update is available after first start
// invoke the Event `tauri://update` from JS or rust side.
handle.listen_global(updater::EVENT_CHECK_UPDATE, move |_msg| {
let handle = handle_.clone();
let package_info = package_info.clone();
let config = config.clone();
// re-spawn task inside tokyo to launch the download
// we don't need to emit anything as everything is handled
// by the process (user is asked to restart at the end)
// and it's handled by the updater
crate::async_runtime::spawn(async move {
updater::check_update_with_dialog(config, package_info, handle).await
});
} else if updater_config.active {
// we only listen for `tauri://update`
// once we receive the call, we check if an update is available or not
// if there is a new update we emit `tauri://update-available` with details
// this is the user responsabilities to display dialog and ask if user want to install
// to install the update you need to invoke the Event `tauri://update-install`
self.listen_updater_events(main_window);
}
});
} else if updater_config.active {
// we only listen for `tauri://update`
// once we receive the call, we check if an update is available or not
// if there is a new update we emit `tauri://update-available` with details
// this is the user responsabilities to display dialog and ask if user want to install
// to install the update you need to invoke the Event `tauri://update-install`
self.listen_updater_events(handle);
}
}
}
Expand Down Expand Up @@ -1330,26 +1330,19 @@ impl<R: Runtime> Builder<R> {
.map(|p| p.label.clone())
.collect::<Vec<_>>();

#[cfg(feature = "updater")]
let mut main_window = None;

for pending in self.pending_windows {
let pending =
app
.manager
.prepare_window(app.handle.clone(), pending, &window_labels, None)?;
let detached = app.runtime.as_ref().unwrap().create_window(pending)?;
let _window = app.manager.attach_window(app.handle(), detached);
#[cfg(feature = "updater")]
if main_window.is_none() {
main_window = Some(_window);
}
}

(self.setup)(&mut app).map_err(|e| crate::Error::Setup(e))?;

#[cfg(feature = "updater")]
app.run_updater(main_window);
app.run_updater();

Ok(app)
}
Expand Down
66 changes: 33 additions & 33 deletions core/tauri/src/updater/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -333,8 +333,8 @@ mod error;
pub use self::error::Error;

use crate::{
api::dialog::blocking::ask, runtime::EventLoopProxy, utils::config::UpdaterConfig, Env,
EventLoopMessage, Manager, Runtime, UpdaterEvent, Window,
api::dialog::blocking::ask, runtime::EventLoopProxy, utils::config::UpdaterConfig, AppHandle,
Env, EventLoopMessage, Manager, Runtime, UpdaterEvent,
};

/// Check for new updates
Expand Down Expand Up @@ -374,14 +374,14 @@ struct UpdateManifest {
pub(crate) async fn check_update_with_dialog<R: Runtime>(
updater_config: UpdaterConfig,
package_info: crate::PackageInfo,
window: Window<R>,
handle: AppHandle<R>,
) {
if let Some(endpoints) = updater_config.endpoints.clone() {
let endpoints = endpoints
.iter()
.map(|e| e.to_string())
.collect::<Vec<String>>();
let env = window.state::<Env>().inner().clone();
let env = handle.state::<Env>().inner().clone();
// check updates
match self::core::builder(env)
.urls(&endpoints[..])
Expand All @@ -395,9 +395,9 @@ pub(crate) async fn check_update_with_dialog<R: Runtime>(
// if dialog enabled only
if updater.should_update && updater_config.dialog {
let body = updater.body.clone().unwrap_or_else(|| String::from(""));
let window_ = window.clone();
let handle_ = handle.clone();
let dialog = prompt_for_install(
window_,
handle_,
&updater.clone(),
&package_info.name,
&body.clone(),
Expand All @@ -406,12 +406,12 @@ pub(crate) async fn check_update_with_dialog<R: Runtime>(
.await;

if let Err(e) = dialog {
send_status_update(window.clone(), UpdaterEvent::Error(e.to_string()));
send_status_update(&handle, UpdaterEvent::Error(e.to_string()));
}
}
}
Err(e) => {
send_status_update(window.clone(), UpdaterEvent::Error(e.to_string()));
send_status_update(&handle, UpdaterEvent::Error(e.to_string()));
}
}
}
Expand All @@ -422,13 +422,13 @@ pub(crate) async fn check_update_with_dialog<R: Runtime>(
pub(crate) fn listener<R: Runtime>(
updater_config: UpdaterConfig,
package_info: crate::PackageInfo,
window: &Window<R>,
handle: &AppHandle<R>,
) {
let isolated_window = window.clone();
let handle_ = handle.clone();

// Wait to receive the event `"tauri://update"`
window.listen(EVENT_CHECK_UPDATE, move |_msg| {
let window = isolated_window.clone();
handle.listen_global(EVENT_CHECK_UPDATE, move |_msg| {
let handle = handle_.clone();
let package_info = package_info.clone();

// prepare our endpoints
Expand All @@ -444,10 +444,10 @@ pub(crate) fn listener<R: Runtime>(

// check updates
crate::async_runtime::spawn(async move {
let window = window.clone();
let window_isolation = window.clone();
let handle = handle.clone();
let handle_ = handle.clone();
let pubkey = pubkey.clone();
let env = window.state::<Env>().inner().clone();
let env = handle.state::<Env>().inner().clone();

match self::core::builder(env)
.urls(&endpoints[..])
Expand All @@ -461,28 +461,27 @@ pub(crate) fn listener<R: Runtime>(
let body = updater.body.clone().unwrap_or_else(|| String::from(""));

// Emit `tauri://update-available`
let _ = window.emit(
let _ = handle.emit_all(
EVENT_UPDATE_AVAILABLE,
UpdateManifest {
body,
date: updater.date.clone(),
version: updater.version.clone(),
},
);
let _ = window
.app_handle
let _ = handle
.create_proxy()
.send_event(EventLoopMessage::Updater(UpdaterEvent::UpdateAvailable));

// Listen for `tauri://update-install`
window.once(EVENT_INSTALL_UPDATE, move |_msg| {
let window = window_isolation.clone();
handle.once_global(EVENT_INSTALL_UPDATE, move |_msg| {
let handle = handle_.clone();
let updater = updater.clone();

// Start installation
crate::async_runtime::spawn(async move {
// emit {"status": "PENDING"}
send_status_update(window.clone(), UpdaterEvent::Pending);
send_status_update(&handle, UpdaterEvent::Pending);

// Launch updater download process
// macOS we display the `Ready to restart dialog` asking to restart
Expand All @@ -492,28 +491,28 @@ pub(crate) fn listener<R: Runtime>(

if let Err(err) = update_result {
// emit {"status": "ERROR", "error": "The error message"}
send_status_update(window.clone(), UpdaterEvent::Error(err.to_string()));
send_status_update(&handle, UpdaterEvent::Error(err.to_string()));
} else {
// emit {"status": "DONE"}
send_status_update(window.clone(), UpdaterEvent::Updated);
send_status_update(&handle, UpdaterEvent::Updated);
}
});
});
} else {
send_status_update(window.clone(), UpdaterEvent::AlreadyUpToDate);
send_status_update(&handle, UpdaterEvent::AlreadyUpToDate);
}
}
Err(e) => {
send_status_update(window.clone(), UpdaterEvent::Error(e.to_string()));
send_status_update(&handle, UpdaterEvent::Error(e.to_string()));
}
}
});
});
}

// Send a status update via `tauri://update-status` event.
fn send_status_update<R: Runtime>(window: Window<R>, message: UpdaterEvent) {
let _ = window.emit(
fn send_status_update<R: Runtime>(handle: &AppHandle<R>, message: UpdaterEvent) {
let _ = handle.emit_all(
EVENT_STATUS_UPDATE,
if let UpdaterEvent::Error(error) = &message {
StatusEvent {
Expand All @@ -527,28 +526,29 @@ fn send_status_update<R: Runtime>(window: Window<R>, message: UpdaterEvent) {
}
},
);
let _ = window
.app_handle
let _ = handle
.create_proxy()
.send_event(EventLoopMessage::Updater(message));
}

// Prompt a dialog asking if the user want to install the new version
// Maybe we should add an option to customize it in future versions.
async fn prompt_for_install<R: Runtime>(
window: Window<R>,
handle: AppHandle<R>,
updater: &self::core::Update,
app_name: &str,
body: &str,
pubkey: String,
) -> crate::Result<()> {
// remove single & double quote
let escaped_body = body.replace(&['\"', '\''][..], "");
let windows = handle.windows();
let parent_window = windows.values().next();

// todo(lemarier): We should review this and make sure we have
// something more conventional.
let should_install = ask(
Some(&window),
parent_window,
format!(r#"A new version of {} is available! "#, app_name),
format!(
r#"{} {} is now available -- you have {}.
Expand All @@ -570,12 +570,12 @@ Release Notes:

// Ask user if we need to restart the application
let should_exit = ask(
Some(&window),
parent_window,
"Ready to Restart",
"The installation was successful, do you want to restart the application now?",
);
if should_exit {
window.app_handle().restart();
handle.restart();
}
}

Expand Down

0 comments on commit c4ca80f

Please sign in to comment.