فهرست منبع

feat(core): use AppHandle instead of Window on the updater logic (#3702)

Lucas Fernandes Nogueira 3 سال پیش
والد
کامیت
c4ca80f919
3فایلهای تغییر یافته به همراه75 افزوده شده و 77 حذف شده
  1. 5 0
      .changes/updater-no-window.md
  2. 37 44
      core/tauri/src/app.rs
  3. 33 33
      core/tauri/src/updater/mod.rs

+ 5 - 0
.changes/updater-no-window.md

@@ -0,0 +1,5 @@
+---
+"tauri": patch
+---
+
+Run the updater on startup even if no window was created.

+ 37 - 44
core/tauri/src/app.rs

@@ -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);
     }
   }
 }
@@ -1330,9 +1330,6 @@ 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
@@ -1340,16 +1337,12 @@ impl<R: Runtime> Builder<R> {
           .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)
   }

+ 33 - 33
core/tauri/src/updater/mod.rs

@@ -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
@@ -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[..])
@@ -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(),
@@ -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()));
       }
     }
   }
@@ -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
@@ -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[..])
@@ -461,7 +461,7 @@ 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,
@@ -469,20 +469,19 @@ pub(crate) fn listener<R: Runtime>(
                 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
@@ -492,19 +491,19 @@ 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()));
         }
       }
     });
@@ -512,8 +511,8 @@ pub(crate) fn listener<R: Runtime>(
 }
 
 // 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 {
@@ -527,8 +526,7 @@ fn send_status_update<R: Runtime>(window: Window<R>, message: UpdaterEvent) {
       }
     },
   );
-  let _ = window
-    .app_handle
+  let _ = handle
     .create_proxy()
     .send_event(EventLoopMessage::Updater(message));
 }
@@ -536,7 +534,7 @@ fn send_status_update<R: Runtime>(window: Window<R>, message: UpdaterEvent) {
 // 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,
@@ -544,11 +542,13 @@ async fn prompt_for_install<R: Runtime>(
 ) -> 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 {}.
@@ -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();
     }
   }