From 4818531aba47e126af91253d5d0eae3972b27d4c Mon Sep 17 00:00:00 2001 From: Lucas Fernandes Nogueira Date: Sat, 5 Feb 2022 00:43:19 -0300 Subject: [PATCH] refactor(core): add blocking dialog APIs, improve docs, closes #3255 (#3270) --- .changes/refactor-dialog-apis.md | 5 + core/tauri/src/api/dialog.rs | 560 ++++++++++++++++++----- core/tauri/src/endpoints/dialog.rs | 39 +- core/tauri/src/endpoints/notification.rs | 8 +- core/tauri/src/updater/mod.rs | 21 +- examples/api/src-tauri/Cargo.lock | 282 ++++++++---- 6 files changed, 668 insertions(+), 247 deletions(-) create mode 100644 .changes/refactor-dialog-apis.md diff --git a/.changes/refactor-dialog-apis.md b/.changes/refactor-dialog-apis.md new file mode 100644 index 00000000000..d78dbeda50d --- /dev/null +++ b/.changes/refactor-dialog-apis.md @@ -0,0 +1,5 @@ +--- +"tauri": patch +--- + +Added the `tauri::api::dialog::blocking` module. diff --git a/core/tauri/src/api/dialog.rs b/core/tauri/src/api/dialog.rs index 699a8c73ec8..e76bf008c04 100644 --- a/core/tauri/src/api/dialog.rs +++ b/core/tauri/src/api/dialog.rs @@ -2,12 +2,13 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT -//! Types and functions related to display dialog. +//! Use native message and file open/save dialogs. +//! +//! This module exposes non-blocking APIs on its root, relying on callback closures +//! to give results back. This is particularly useful when running dialogs from the main thread. +//! When using on asynchronous contexts such as async commands, the [`blocking`] APIs are recommended. -#[cfg(any(dialog_open, dialog_save))] -use std::path::{Path, PathBuf}; - -use crate::{Runtime, Window}; +pub use nonblocking::*; #[cfg(not(target_os = "linux"))] macro_rules! run_dialog { @@ -32,142 +33,461 @@ macro_rules! run_dialog { }}; } -/// The file dialog builder. +macro_rules! run_dialog_sync { + ($e:expr) => {{ + let (tx, rx) = sync_channel(0); + let cb = move |response| { + tx.send(response).unwrap(); + }; + run_dialog!($e, cb); + rx.recv().unwrap() + }}; +} + +macro_rules! file_dialog_builder { + () => { + /// The file dialog builder. + /// + /// Constructs file picker dialogs that can select single/multiple files or directories. + #[cfg(any(dialog_open, dialog_save))] + #[derive(Debug, Default)] + pub struct FileDialogBuilder(rfd::FileDialog); + + #[cfg(any(dialog_open, dialog_save))] + impl FileDialogBuilder { + /// Gets the default file dialog builder. + pub fn new() -> Self { + Default::default() + } + + /// Add file extension filter. Takes in the name of the filter, and list of extensions + #[must_use] + pub fn add_filter(mut self, name: impl AsRef, extensions: &[&str]) -> Self { + self.0 = self.0.add_filter(name.as_ref(), extensions); + self + } + + /// Set starting directory of the dialog. + #[must_use] + pub fn set_directory>(mut self, directory: P) -> Self { + self.0 = self.0.set_directory(directory); + self + } + + /// Set starting file name of the dialog. + #[must_use] + pub fn set_file_name(mut self, file_name: &str) -> Self { + self.0 = self.0.set_file_name(file_name); + self + } + + /// Sets the parent window of the dialog. + #[must_use] + pub fn set_parent(mut self, parent: &W) -> Self { + self.0 = self.0.set_parent(parent); + self + } + + /// Set the title of the dialog. + #[must_use] + pub fn set_title(mut self, title: &str) -> Self { + self.0 = self.0.set_title(title); + self + } + } + }; +} + +/// Blocking interfaces for the dialog APIs. /// -/// Constructs file picker dialogs that can select single/multiple files or directories. -#[cfg(any(dialog_open, dialog_save))] -#[derive(Debug, Default)] -pub struct FileDialogBuilder(rfd::FileDialog); - -#[cfg(any(dialog_open, dialog_save))] -impl FileDialogBuilder { - /// Gets the default file dialog builder. - pub fn new() -> Self { - Default::default() - } +/// The blocking APIs will block the current thread to execute instead of relying on callback closures, +/// which makes them easier to use. +/// +/// **NOTE:** You cannot block the main thread when executing the dialog APIs, so you must use the [`tauri::api::dialog`] methods instead. +/// Examples of main thread context are the [`tauri::App::run`] closure and non-async commmands. +pub mod blocking { + use crate::{Runtime, Window}; + #[cfg(any(dialog_open, dialog_save))] + use std::path::{Path, PathBuf}; + use std::sync::mpsc::sync_channel; - /// Add file extension filter. Takes in the name of the filter, and list of extensions - #[must_use] - pub fn add_filter(mut self, name: impl AsRef, extensions: &[&str]) -> Self { - self.0 = self.0.add_filter(name.as_ref(), extensions); - self - } + file_dialog_builder!(); - /// Set starting directory of the dialog. - #[must_use] - pub fn set_directory>(mut self, directory: P) -> Self { - self.0 = self.0.set_directory(directory); - self - } + impl FileDialogBuilder { + /// Shows the dialog to select a single file. + /// This is a blocking operation, + /// and should *NOT* be used when running on the main thread context. + /// + /// # Example + /// + /// ```rust,no_run + /// use tauri::api::dialog::blocking::FileDialogBuilder; + /// #[tauri::command] + /// fn my_command() { + /// let file_path = FileDialogBuilder::new().pick_file(); + /// // do something with the optional file path here + /// // the file path is `None` if the user closed the dialog + /// } + /// ``` + pub fn pick_file(self) -> Option { + run_dialog_sync!(self.0.pick_file()) + } - /// Set starting file name of the dialog. - #[must_use] - pub fn set_file_name(mut self, file_name: &str) -> Self { - self.0 = self.0.set_file_name(file_name); - self - } + /// Shows the dialog to select multiple files. + /// This is a blocking operation, + /// and should *NOT* be used when running on the main thread context. + /// + /// # Example + /// + /// ```rust,no_run + /// use tauri::api::dialog::blocking::FileDialogBuilder; + /// #[tauri::command] + /// fn my_command() { + /// let file_path = FileDialogBuilder::new().pick_files(); + /// // do something with the optional file paths here + /// // the file paths value is `None` if the user closed the dialog + /// } + /// ``` + pub fn pick_files(self) -> Option> { + run_dialog_sync!(self.0.pick_files()) + } - /// Sets the parent window of the dialog. - #[must_use] - pub fn set_parent(mut self, parent: &W) -> Self { - self.0 = self.0.set_parent(parent); - self - } + /// Shows the dialog to select a single folder. + /// This is a blocking operation, + /// and should *NOT* be used when running on the main thread context. + /// + /// # Example + /// + /// ```rust,no_run + /// use tauri::api::dialog::blocking::FileDialogBuilder; + /// #[tauri::command] + /// fn my_command() { + /// let folder_path = FileDialogBuilder::new().pick_folder(); + /// // do something with the optional folder path here + /// // the folder path is `None` if the user closed the dialog + /// } + /// ``` + pub fn pick_folder(self) -> Option { + run_dialog_sync!(self.0.pick_folder()) + } - /// Set the title of the dialog. - pub fn set_title(mut self, title: &str) -> Self { - self.0 = self.0.set_title(title); - self + /// Shows the dialog to save a file. + /// This is a blocking operation, + /// and should *NOT* be used when running on the main thread context. + /// + /// # Example + /// + /// ```rust,no_run + /// use tauri::api::dialog::blocking::FileDialogBuilder; + /// #[tauri::command] + /// fn my_command() { + /// let file_path = FileDialogBuilder::new().save_file(); + /// // do something with the optional file path here + /// // the file path is `None` if the user closed the dialog + /// } + /// ``` + pub fn save_file(self) -> Option { + run_dialog_sync!(self.0.save_file()) + } } - /// Pick one file. - pub fn pick_file) + Send + 'static>(self, f: F) { - run_dialog!(self.0.pick_file(), f) + /// Displays a dialog with a message and an optional title with a "yes" and a "no" button and wait for it to be closed. + /// + /// This is a blocking operation, + /// and should *NOT* be used when running on the main thread context. + /// + /// # Example + /// + /// ```rust,no_run + /// use tauri::api::dialog::blocking::ask; + /// # let app = tauri::Builder::default().build(tauri::generate_context!("test/fixture/src-tauri/tauri.conf.json")).unwrap(); + /// # let window = tauri::Manager::get_window(&app, "main").unwrap(); + /// let answer = ask(Some(&window), "Tauri", "Is Tauri awesome?"); + /// // do something with `answer` + /// ``` + #[allow(unused_variables)] + pub fn ask( + parent_window: Option<&Window>, + title: impl AsRef, + message: impl AsRef, + ) -> bool { + run_message_dialog(parent_window, title, message, rfd::MessageButtons::YesNo) } - /// Pick multiple files. - pub fn pick_files>) + Send + 'static>(self, f: F) { - run_dialog!(self.0.pick_files(), f) + /// Displays a dialog with a message and an optional title with an "ok" and a "cancel" button and wait for it to be closed. + /// + /// This is a blocking operation, + /// and should *NOT* be used when running on the main thread context. + /// + /// # Example + /// + /// ```rust,no_run + /// use tauri::api::dialog::blocking::confirm; + /// # let app = tauri::Builder::default().build(tauri::generate_context!("test/fixture/src-tauri/tauri.conf.json")).unwrap(); + /// # let window = tauri::Manager::get_window(&app, "main").unwrap(); + /// let answer = confirm(Some(&window), "Tauri", "Are you sure?"); + /// // do something with `answer` + /// ``` + #[allow(unused_variables)] + pub fn confirm( + parent_window: Option<&Window>, + title: impl AsRef, + message: impl AsRef, + ) -> bool { + run_message_dialog(parent_window, title, message, rfd::MessageButtons::OkCancel) } - /// Pick one folder. - pub fn pick_folder) + Send + 'static>(self, f: F) { - run_dialog!(self.0.pick_folder(), f) + /// Displays a message dialog and wait for it to be closed. + /// + /// This is a blocking operation, + /// and should *NOT* be used when running on the main thread context. + /// + /// # Example + /// + /// ```rust,no_run + /// use tauri::api::dialog::blocking::message; + /// # let app = tauri::Builder::default().build(tauri::generate_context!("test/fixture/src-tauri/tauri.conf.json")).unwrap(); + /// # let window = tauri::Manager::get_window(&app, "main").unwrap(); + /// message(Some(&window), "Tauri", "Tauri is awesome!"); + /// ``` + #[allow(unused_variables)] + pub fn message( + parent_window: Option<&Window>, + title: impl AsRef, + message: impl AsRef, + ) { + let _ = run_message_dialog(parent_window, title, message, rfd::MessageButtons::Ok); } - /// Opens save file dialog. - pub fn save_file) + Send + 'static>(self, f: F) { - run_dialog!(self.0.save_file(), f) + #[allow(unused_variables)] + fn run_message_dialog( + parent_window: Option<&Window>, + title: impl AsRef, + message: impl AsRef, + buttons: rfd::MessageButtons, + ) -> bool { + let (tx, rx) = sync_channel(1); + super::nonblocking::run_message_dialog( + parent_window, + title, + message, + buttons, + move |response| { + tx.send(response).unwrap(); + }, + ); + rx.recv().unwrap() } } -/// Displays a dialog with a message and an optional title with a "yes" and a "no" button. -#[allow(unused_variables)] -pub fn ask( - parent_window: Option<&Window>, - title: impl AsRef, - message: impl AsRef, - f: F, -) { - run_message_dialog(parent_window, title, message, rfd::MessageButtons::YesNo, f) -} +mod nonblocking { + use crate::{Runtime, Window}; + #[cfg(any(dialog_open, dialog_save))] + use std::path::{Path, PathBuf}; -/// Displays a dialog with a message and an optional title with an "ok" and a "cancel" button. -#[allow(unused_variables)] -pub fn confirm( - parent_window: Option<&Window>, - title: impl AsRef, - message: impl AsRef, - f: F, -) { - run_message_dialog( - parent_window, - title, - message, - rfd::MessageButtons::OkCancel, - f, - ) -} + file_dialog_builder!(); -/// Displays a message dialog. -#[allow(unused_variables)] -pub fn message( - parent_window: Option<&Window>, - title: impl AsRef, - message: impl AsRef, -) { - run_message_dialog( - parent_window, - title, - message, - rfd::MessageButtons::Ok, - |_| {}, - ) -} + impl FileDialogBuilder { + /// Shows the dialog to select a single file. + /// This is not a blocking operation, + /// and should be used when running on the main thread to avoid deadlocks with the event loop. + /// + /// For usage in other contexts such as commands, prefer [`Self::pick_file`]. + /// + /// # Example + /// + /// ```rust,no_run + /// use tauri::api::dialog::FileDialogBuilder; + /// tauri::Builder::default() + /// .build(tauri::generate_context!("test/fixture/src-tauri/tauri.conf.json")) + /// .expect("failed to build tauri app") + /// .run(|_app, _event| { + /// FileDialogBuilder::new().pick_file(|file_path| { + /// // do something with the optional file path here + /// // the file path is `None` if the user closed the dialog + /// }) + /// }) + /// ``` + pub fn pick_file) + Send + 'static>(self, f: F) { + run_dialog!(self.0.pick_file(), f) + } + + /// Shows the dialog to select multiple files. + /// This is not a blocking operation, + /// and should be used when running on the main thread to avoid deadlocks with the event loop. + /// + /// # Example + /// + /// ```rust,no_run + /// use tauri::api::dialog::FileDialogBuilder; + /// tauri::Builder::default() + /// .build(tauri::generate_context!("test/fixture/src-tauri/tauri.conf.json")) + /// .expect("failed to build tauri app") + /// .run(|_app, _event| { + /// FileDialogBuilder::new().pick_files(|file_paths| { + /// // do something with the optional file paths here + /// // the file paths value is `None` if the user closed the dialog + /// }) + /// }) + /// ``` + pub fn pick_files>) + Send + 'static>(self, f: F) { + run_dialog!(self.0.pick_files(), f) + } + + /// Shows the dialog to select a single folder. + /// This is not a blocking operation, + /// and should be used when running on the main thread to avoid deadlocks with the event loop. + /// + /// # Example + /// + /// ```rust,no_run + /// use tauri::api::dialog::FileDialogBuilder; + /// tauri::Builder::default() + /// .build(tauri::generate_context!("test/fixture/src-tauri/tauri.conf.json")) + /// .expect("failed to build tauri app") + /// .run(|_app, _event| { + /// FileDialogBuilder::new().pick_folder(|folder_path| { + /// // do something with the optional folder path here + /// // the folder path is `None` if the user closed the dialog + /// }) + /// }) + /// ``` + pub fn pick_folder) + Send + 'static>(self, f: F) { + run_dialog!(self.0.pick_folder(), f) + } -#[allow(unused_variables)] -fn run_message_dialog( - parent_window: Option<&Window>, - title: impl AsRef, - message: impl AsRef, - buttons: rfd::MessageButtons, - f: F, -) { - let title = title.as_ref().to_string(); - let message = message.as_ref().to_string(); - #[allow(unused_mut)] - let mut builder = rfd::MessageDialog::new() - .set_title(&title) - .set_description(&message) - .set_buttons(buttons) - .set_level(rfd::MessageLevel::Info); - - #[cfg(any(windows, target_os = "macos"))] - { - if let Some(window) = parent_window { - builder = builder.set_parent(window); + /// Shows the dialog to save a file. + /// + /// This is not a blocking operation, + /// and should be used when running on the main thread to avoid deadlocks with the event loop. + /// + /// # Example + /// + /// ```rust,no_run + /// use tauri::api::dialog::FileDialogBuilder; + /// tauri::Builder::default() + /// .build(tauri::generate_context!("test/fixture/src-tauri/tauri.conf.json")) + /// .expect("failed to build tauri app") + /// .run(|_app, _event| { + /// FileDialogBuilder::new().save_file(|file_path| { + /// // do something with the optional file path here + /// // the file path is `None` if the user closed the dialog + /// }) + /// }) + /// ``` + pub fn save_file) + Send + 'static>(self, f: F) { + run_dialog!(self.0.save_file(), f) } } - run_dialog!(builder.show(), f) + /// Displays a non-blocking dialog with a message and an optional title with a "yes" and a "no" button. + /// + /// This is not a blocking operation, + /// and should be used when running on the main thread to avoid deadlocks with the event loop. + /// + /// # Example + /// + /// ```rust,no_run + /// use tauri::api::dialog::ask; + /// # let app = tauri::Builder::default().build(tauri::generate_context!("test/fixture/src-tauri/tauri.conf.json")).unwrap(); + /// # let window = tauri::Manager::get_window(&app, "main").unwrap(); + /// ask(Some(&window), "Tauri", "Is Tauri awesome?", |answer| { + /// // do something with `answer` + /// }); + /// ``` + #[allow(unused_variables)] + pub fn ask( + parent_window: Option<&Window>, + title: impl AsRef, + message: impl AsRef, + f: F, + ) { + run_message_dialog(parent_window, title, message, rfd::MessageButtons::YesNo, f) + } + + /// Displays a non-blocking dialog with a message and an optional title with an "ok" and a "cancel" button. + /// + /// This is not a blocking operation, + /// and should be used when running on the main thread to avoid deadlocks with the event loop. + /// + /// # Example + /// + /// ```rust,no_run + /// use tauri::api::dialog::confirm; + /// # let app = tauri::Builder::default().build(tauri::generate_context!("test/fixture/src-tauri/tauri.conf.json")).unwrap(); + /// # let window = tauri::Manager::get_window(&app, "main").unwrap(); + /// confirm(Some(&window), "Tauri", "Are you sure?", |answer| { + /// // do something with `answer` + /// }); + /// ``` + #[allow(unused_variables)] + pub fn confirm( + parent_window: Option<&Window>, + title: impl AsRef, + message: impl AsRef, + f: F, + ) { + run_message_dialog( + parent_window, + title, + message, + rfd::MessageButtons::OkCancel, + f, + ) + } + + /// Displays a non-blocking message dialog. + /// + /// This is not a blocking operation, + /// and should be used when running on the main thread to avoid deadlocks with the event loop. + /// + /// # Example + /// + /// ```rust,no_run + /// use tauri::api::dialog::message; + /// # let app = tauri::Builder::default().build(tauri::generate_context!("test/fixture/src-tauri/tauri.conf.json")).unwrap(); + /// # let window = tauri::Manager::get_window(&app, "main").unwrap(); + /// message(Some(&window), "Tauri", "Tauri is awesome!"); + /// ``` + #[allow(unused_variables)] + pub fn message( + parent_window: Option<&Window>, + title: impl AsRef, + message: impl AsRef, + ) { + run_message_dialog( + parent_window, + title, + message, + rfd::MessageButtons::Ok, + |_| {}, + ) + } + + #[allow(unused_variables)] + pub(crate) fn run_message_dialog( + parent_window: Option<&Window>, + title: impl AsRef, + message: impl AsRef, + buttons: rfd::MessageButtons, + f: F, + ) { + let title = title.as_ref().to_string(); + let message = message.as_ref().to_string(); + #[allow(unused_mut)] + let mut builder = rfd::MessageDialog::new() + .set_title(&title) + .set_description(&message) + .set_buttons(buttons) + .set_level(rfd::MessageLevel::Info); + + #[cfg(any(windows, target_os = "macos"))] + { + if let Some(window) = parent_window { + builder = builder.set_parent(window); + } + } + + run_dialog!(builder.show(), f) + } } diff --git a/core/tauri/src/endpoints/dialog.rs b/core/tauri/src/endpoints/dialog.rs index 3605586d7f9..9c72165ca34 100644 --- a/core/tauri/src/endpoints/dialog.rs +++ b/core/tauri/src/endpoints/dialog.rs @@ -4,14 +4,12 @@ use super::{InvokeContext, InvokeResponse}; #[cfg(any(dialog_open, dialog_save))] -use crate::api::dialog::FileDialogBuilder; +use crate::api::dialog::blocking::FileDialogBuilder; use crate::Runtime; use serde::Deserialize; use tauri_macros::{module_command_handler, CommandModule}; use std::path::PathBuf; -#[cfg(any(dialog_message, dialog_ask, dialog_confirm))] -use std::sync::mpsc::channel; #[allow(dead_code)] #[derive(Debug, Clone, Deserialize)] @@ -99,17 +97,15 @@ impl Cmd { dialog_builder = dialog_builder.add_filter(filter.name, &extensions); } - let (tx, rx) = channel(); - - if options.directory { - dialog_builder.pick_folder(move |p| tx.send(p.into()).unwrap()); + let res = if options.directory { + dialog_builder.pick_folder().into() } else if options.multiple { - dialog_builder.pick_files(move |p| tx.send(p.into()).unwrap()); + dialog_builder.pick_files().into() } else { - dialog_builder.pick_file(move |p| tx.send(p.into()).unwrap()); - } + dialog_builder.pick_file().into() + }; - Ok(rx.recv().unwrap()) + Ok(res) } #[module_command_handler(dialog_save, "dialog > save")] @@ -130,14 +126,13 @@ impl Cmd { let extensions: Vec<&str> = filter.extensions.iter().map(|s| &**s).collect(); dialog_builder = dialog_builder.add_filter(filter.name, &extensions); } - let (tx, rx) = channel(); - dialog_builder.save_file(move |p| tx.send(p).unwrap()); - Ok(rx.recv().unwrap()) + + Ok(dialog_builder.save_file()) } #[module_command_handler(dialog_message, "dialog > message")] fn message_dialog(context: InvokeContext, message: String) -> crate::Result<()> { - crate::api::dialog::message( + crate::api::dialog::blocking::message( Some(&context.window), &context.window.app_handle.package_info().name, message, @@ -151,14 +146,11 @@ impl Cmd { title: Option, message: String, ) -> crate::Result { - let (tx, rx) = channel(); - crate::api::dialog::ask( + Ok(crate::api::dialog::blocking::ask( Some(&context.window), title.unwrap_or_else(|| context.window.app_handle.package_info().name.clone()), message, - move |m| tx.send(m).unwrap(), - ); - Ok(rx.recv().unwrap()) + )) } #[module_command_handler(dialog_confirm, "dialog > confirm")] @@ -167,14 +159,11 @@ impl Cmd { title: Option, message: String, ) -> crate::Result { - let (tx, rx) = channel(); - crate::api::dialog::confirm( + Ok(crate::api::dialog::blocking::confirm( Some(&context.window), title.unwrap_or_else(|| context.window.app_handle.package_info().name.clone()), message, - move |m| tx.send(m).unwrap(), - ); - Ok(rx.recv().unwrap()) + )) } } diff --git a/core/tauri/src/endpoints/notification.rs b/core/tauri/src/endpoints/notification.rs index 2ffb07904ca..c0b4b78eb0a 100644 --- a/core/tauri/src/endpoints/notification.rs +++ b/core/tauri/src/endpoints/notification.rs @@ -111,18 +111,12 @@ fn request_permission(context: &InvokeContext) -> bool { if let Some(allow_notification) = settings.allow_notification { return allow_notification; } - let (tx, rx) = std::sync::mpsc::channel(); - crate::api::dialog::ask( + let answer = crate::api::dialog::blocking::ask( Some(&context.window), "Permissions", "This app wants to show notifications. Do you allow?", - move |answer| { - tx.send(answer).unwrap(); - }, ); - let answer = rx.recv().unwrap(); - settings.allow_notification = Some(answer); let _ = crate::settings::write_settings( &context.config, diff --git a/core/tauri/src/updater/mod.rs b/core/tauri/src/updater/mod.rs index b9bc97c4323..aea8f65f5d8 100644 --- a/core/tauri/src/updater/mod.rs +++ b/core/tauri/src/updater/mod.rs @@ -333,14 +333,12 @@ mod error; pub use self::error::Error; use crate::{ - api::{dialog::ask, process::restart}, + api::{dialog::blocking::ask, process::restart}, runtime::Runtime, utils::config::UpdaterConfig, Env, Manager, Window, }; -use std::sync::mpsc::channel; - /// Check for new updates pub const EVENT_CHECK_UPDATE: &str = "tauri://update"; /// New update available @@ -539,11 +537,9 @@ async fn prompt_for_install( // remove single & double quote let escaped_body = body.replace(&['\"', '\''][..], ""); - let (tx, rx) = channel(); - // todo(lemarier): We should review this and make sure we have // something more conventional. - ask( + let should_install = ask( Some(&window), format!(r#"A new version of {} is available! "#, app_name), format!( @@ -555,10 +551,9 @@ Release Notes: {}"#, app_name, updater.version, updater.current_version, escaped_body, ), - move |should_install| tx.send(should_install).unwrap(), ); - if rx.recv().unwrap() { + if should_install { // Launch updater download process // macOS we display the `Ready to restart dialog` asking to restart // Windows is closing the current App and launch the downloaded MSI when ready (the process stop here) @@ -567,16 +562,14 @@ Release Notes: // Ask user if we need to restart the application let env = window.state::().inner().clone(); - ask( + let should_exit = ask( Some(&window), "Ready to Restart", "The installation was successful, do you want to restart the application now?", - move |should_exit| { - if should_exit { - restart(&env); - } - }, ); + if should_exit { + restart(&env); + } } Ok(()) diff --git a/examples/api/src-tauri/Cargo.lock b/examples/api/src-tauri/Cargo.lock index c6a3faf7406..1f926c4659b 100644 --- a/examples/api/src-tauri/Cargo.lock +++ b/examples/api/src-tauri/Cargo.lock @@ -29,7 +29,7 @@ version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "cipher", "cpufeatures", "opaque-debug", @@ -101,6 +101,42 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" +[[package]] +name = "async-broadcast" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90622698a1218e0b2fb846c97b5f19a0831f6baddee73d9454156365ccfa473b" +dependencies = [ + "easy-parallel", + "event-listener", + "futures-core", +] + +[[package]] +name = "async-channel" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2114d64672151c0c5eaa5e131ec84a74f06e1e559830dabba01ca30605d66319" +dependencies = [ + "concurrent-queue", + "event-listener", + "futures-core", +] + +[[package]] +name = "async-executor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "871f9bb5e0a22eeb7e8cf16641feb87c9dc67032ccf8ff49e772eb9941d3a965" +dependencies = [ + "async-task", + "concurrent-queue", + "fastrand", + "futures-lite", + "once_cell", + "slab", +] + [[package]] name = "async-io" version = "1.6.0" @@ -120,6 +156,43 @@ dependencies = [ "winapi", ] +[[package]] +name = "async-lock" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6a8ea61bf9947a1007c5cada31e647dbc77b103c679858150003ba697ea798b" +dependencies = [ + "event-listener", +] + +[[package]] +name = "async-recursion" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7d78656ba01f1b93024b7c3a0467f1608e4be67d725749fdcd7d2c7678fd7a2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "async-task" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d306121baf53310a3fd342d88dc0824f6bbeace68347593658525565abee8" + +[[package]] +name = "async-trait" +version = "0.1.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "061a7acccaa286c011ddc30970520b98fa40e00c9d644633fb26b5fc63a265e3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "atk" version = "0.14.0" @@ -220,7 +293,7 @@ dependencies = [ "arrayref", "arrayvec 0.7.2", "cc", - "cfg-if 1.0.0", + "cfg-if", "constant_time_eq", "digest 0.10.1", "rayon", @@ -243,9 +316,9 @@ dependencies = [ [[package]] name = "block-buffer" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1d36a02058e76b040de25a4464ba1c80935655595b661505c8b39b664828b95" +checksum = "03588e54c62ae6d763e2a80090d50353b785795361b4ff5b3bf0a5097fc31c0b" dependencies = [ "generic-array", ] @@ -376,12 +449,6 @@ dependencies = [ "smallvec", ] -[[package]] -name = "cfg-if" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" - [[package]] name = "cfg-if" version = "1.0.0" @@ -539,7 +606,7 @@ version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2209c310e29876f7f0b2721e7e26b84aff178aa3da5d091f9bfbf47669e60e3" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", ] [[package]] @@ -548,7 +615,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e54ea8bc3fb1ee042f5aace6e3c6e025d3874866da222930f70ce62aceba0bfa" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "crossbeam-utils", ] @@ -558,7 +625,7 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "crossbeam-epoch", "crossbeam-utils", ] @@ -569,7 +636,7 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97242a70df9b89a65d0b6df3c4bf5b9ce03c5b7309019777fbde37e7537f8762" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "crossbeam-utils", "lazy_static", "memoffset", @@ -582,7 +649,7 @@ version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfcae03edb34f947e64acdb1c33ec169824e20657e9ecb61cef6c8c74dcb8120" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "lazy_static", ] @@ -776,7 +843,7 @@ version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b697d66081d42af4fba142d56918a3cb21dc8eb63372c6b85d14f44fb9c5979b" dependencies = [ - "block-buffer 0.10.0", + "block-buffer 0.10.1", "crypto-common", "generic-array", "subtle", @@ -799,7 +866,7 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "dirs-sys-next", ] @@ -835,6 +902,12 @@ dependencies = [ "dtoa", ] +[[package]] +name = "easy-parallel" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6907e25393cdcc1f4f3f513d9aac1e840eb1cc341a0fccb01171f7d14d10b946" + [[package]] name = "either" version = "1.6.1" @@ -849,9 +922,9 @@ checksum = "4ef6b89e5b37196644d8796de5268852ff179b44e96276cf4290264843743bb7" [[package]] name = "enumflags2" -version = "0.6.4" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83c8d82922337cd23a15f88b70d8e4ef5f11da38dd7cdb55e84dd5de99695da0" +checksum = "a25c90b056b3f84111cf183cbeddef0d3a0bbe9a674f057e1a1533c315f24def" dependencies = [ "enumflags2_derive", "serde", @@ -859,15 +932,21 @@ dependencies = [ [[package]] name = "enumflags2_derive" -version = "0.6.4" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "946ee94e3dbf58fdd324f9ce245c7b238d46a66f00e86a020b71996349e46cce" +checksum = "144ec79496cbab6f84fa125dc67be9264aef22eb8a28da8454d9c33f15108da4" dependencies = [ "proc-macro2", "quote", "syn", ] +[[package]] +name = "event-listener" +version = "2.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77f3309417938f28bf8228fcff79a4a37103981e3e186d2ccd19c74b38f4eb71" + [[package]] name = "fastrand" version = "1.7.0" @@ -893,7 +972,7 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "975ccf83d8d9d0d84682850a38c8169027be83368805971cc4f238c2b245bc98" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "libc", "redox_syscall 0.2.10", "winapi", @@ -905,7 +984,7 @@ version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e6988e897c1c9c485f43b47a529cef42fde0547f9d8d41a7062518f1d8fc53f" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "crc32fast", "libc", "miniz_oxide 0.4.4", @@ -1165,7 +1244,7 @@ version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "libc", "wasi 0.9.0+wasi-snapshot-preview1", ] @@ -1176,7 +1255,7 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "418d37c8b1d42553c93648be529cb70f920d3baf8ef469b74b9638df426e0b4c" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "libc", "wasi 0.10.2+wasi-snapshot-preview1", ] @@ -1381,6 +1460,12 @@ dependencies = [ "libc", ] +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + [[package]] name = "html5ever" version = "0.25.1" @@ -1491,7 +1576,7 @@ version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", ] [[package]] @@ -1635,7 +1720,7 @@ version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", ] [[package]] @@ -1644,7 +1729,7 @@ version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "edc5c7d328e32cc4954e8e01193d7f0ef5ab257b5090b70a964e099a36034309" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "generator", "scoped-tls", "serde", @@ -1767,16 +1852,6 @@ dependencies = [ "tempfile", ] -[[package]] -name = "nb-connect" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1bb540dc6ef51cfe1916ec038ce7a620daf3a111e2502d745197cd53d6bca15" -dependencies = [ - "libc", - "socket2", -] - [[package]] name = "ndk" version = "0.4.0" @@ -1831,15 +1906,15 @@ checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" [[package]] name = "nix" -version = "0.17.0" +version = "0.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50e4785f2c3b7589a0d0c1dd60285e1188adac4006e8abd6dd578e1567027363" +checksum = "9f866317acbd3a240710c63f065ffb1e4fd466259045ccb504130b7f668f35c6" dependencies = [ "bitflags", "cc", - "cfg-if 0.1.10", + "cfg-if", "libc", - "void", + "memoffset", ] [[package]] @@ -1850,9 +1925,9 @@ checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" [[package]] name = "notify-rust" -version = "4.5.5" +version = "4.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca6ebab865e67efdd7182a88d76cadbdd2a8d02d1c7a4e16bb7c234016a12cac" +checksum = "367e1355a950d3e758e414f3ca1b3981a57a2aa1fa3338eb0059f5b230b6ffa4" dependencies = [ "mac-notification-sys", "serde", @@ -1991,7 +2066,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c7ae222234c30df141154f159066c5093ff73b63204dcda7121eb082fc56a95" dependencies = [ "bitflags", - "cfg-if 1.0.0", + "cfg-if", "foreign-types", "libc", "once_cell", @@ -2017,6 +2092,16 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "ordered-stream" +version = "0.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44630c059eacfd6e08bdaa51b1db2ce33119caa4ddc1235e923109aa5f25ccb1" +dependencies = [ + "futures-core", + "pin-project-lite", +] + [[package]] name = "os_info" version = "3.2.0" @@ -2095,7 +2180,7 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "instant", "libc", "redox_syscall 0.2.10", @@ -2270,7 +2355,7 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "685404d509889fade3e86fe3a5803bca2ec09b0c0778d5ada6ec8bf7a8de5259" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "libc", "log", "wepoll-ffi", @@ -2283,7 +2368,7 @@ version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8419d2b623c7c0896ff2d5d96e2cb4ede590fed28fcc34934f4c33c036e620a1" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "cpufeatures", "opaque-debug", "universal-hash", @@ -2629,7 +2714,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ - "semver 1.0.4", + "semver 1.0.5", ] [[package]] @@ -2729,9 +2814,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "568a8e6258aa33c13358f81fd834adb854c6f7c9468520910a9b1e8fac068012" +checksum = "0486718e92ec9a68fbed73bb5ef687d71103b142595b406835649bebd33f72c7" [[package]] name = "semver-parser" @@ -2849,6 +2934,21 @@ dependencies = [ "stable_deref_trait", ] +[[package]] +name = "sha1" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1da05c97445caa12d05e848c4a4fcbbea29e748ac28f7e80e9b010392063770" +dependencies = [ + "sha1_smol", +] + +[[package]] +name = "sha1_smol" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012" + [[package]] name = "sha2" version = "0.9.9" @@ -2856,7 +2956,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" dependencies = [ "block-buffer 0.9.0", - "cfg-if 1.0.0", + "cfg-if", "cpufeatures", "digest 0.9.0", "opaque-debug", @@ -3088,7 +3188,7 @@ dependencies = [ [[package]] name = "tao" version = "0.5.2" -source = "git+https://github.com/tauri-apps/tao?branch=next#cf3d3b54ae3e32b4b7577240a93510f46c30593f" +source = "git+https://github.com/tauri-apps/tao?branch=next#6ee36748252e3ce26e0341464ebbab1d1adb8c28" dependencies = [ "bitflags", "cairo-rs", @@ -3132,7 +3232,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "271450eb289cb4d8d0720c6ce70c72c8c858c93dd61fc625881616752e6b98f6" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "core-foundation-sys", "libc", "objc", @@ -3180,7 +3280,7 @@ dependencies = [ "raw-window-handle", "regex", "rfd", - "semver 1.0.4", + "semver 1.0.5", "serde", "serde_json", "serde_repr", @@ -3306,7 +3406,7 @@ version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "fastrand", "libc", "redox_syscall 0.2.10", @@ -3427,7 +3527,7 @@ version = "0.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d8d93354fe2a8e50d5953f5ae2e47a3fc2ef03292e7ea46e3cc38f549525fb9" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "pin-project-lite", "tracing-attributes", "tracing-core", @@ -3467,9 +3567,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.7" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5312f325fe3588e277415f5a6cca1f4ccad0f248c4cd5a4bd33032d7286abc22" +checksum = "74786ce43333fcf51efe947aed9718fbe46d5c7328ec3f1029e818083966d9aa" dependencies = [ "ansi_term", "lazy_static", @@ -3599,12 +3699,6 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" -[[package]] -name = "void" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" - [[package]] name = "waker-fn" version = "1.1.0" @@ -3640,7 +3734,7 @@ version = "0.2.79" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25f1af7423d8588a3d840681122e72e6a24ddbcb3f0ec385cac0d12d24256c06" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "wasm-bindgen-macro", ] @@ -3665,7 +3759,7 @@ version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2eb6ec270a31b1d3c7e266b999739109abce8b6c87e4b31fcfcd788b65267395" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "js-sys", "wasm-bindgen", "web-sys", @@ -4093,39 +4187,65 @@ checksum = "d2d7d3948613f75c98fd9328cfdcc45acc4d360655289d0a7d4ec931392200a3" [[package]] name = "zbus" -version = "1.9.1" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2326acc379a3ac4e34b794089f5bdb17086bf29a5fdf619b7b4cc772dc2e9dad" +checksum = "7bb86f3d4592e26a48b2719742aec94f8ae6238ebde20d98183ee185d1275e9a" dependencies = [ + "async-broadcast", + "async-channel", + "async-executor", "async-io", + "async-lock", + "async-recursion", + "async-task", + "async-trait", "byteorder", "derivative", "enumflags2", - "fastrand", - "futures", - "nb-connect", + "event-listener", + "futures-core", + "futures-sink", + "futures-util", + "hex", + "lazy_static", "nix", "once_cell", - "polling", - "scoped-tls", + "ordered-stream", + "rand 0.8.4", "serde", "serde_repr", + "sha1", + "static_assertions", + "winapi", "zbus_macros", + "zbus_names", "zvariant", ] [[package]] name = "zbus_macros" -version = "1.9.1" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a482c56029e48681b89b92b5db3c446db0915e8dd1052c0328a574eda38d5f93" +checksum = "36823cc10fddc3c6b19f048903262dacaf8274170e9a255784bdd8b4570a8040" dependencies = [ - "proc-macro-crate 0.1.5", + "proc-macro-crate 1.1.0", "proc-macro2", "quote", + "regex", "syn", ] +[[package]] +name = "zbus_names" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45dfcdcf87b71dad505d30cc27b1b7b88a64b6d1c435648f48f9dbc1fdc4b7e1" +dependencies = [ + "serde", + "static_assertions", + "zvariant", +] + [[package]] name = "zip" version = "0.5.13" @@ -4171,9 +4291,9 @@ dependencies = [ [[package]] name = "zvariant" -version = "2.10.0" +version = "3.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a68c7b55f2074489b7e8e07d2d0a6ee6b4f233867a653c664d8020ba53692525" +checksum = "49ea5dc38b2058fae6a5b79009388143dadce1e91c26a67f984a0fc0381c8033" dependencies = [ "byteorder", "enumflags2", @@ -4185,9 +4305,9 @@ dependencies = [ [[package]] name = "zvariant_derive" -version = "2.10.0" +version = "3.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4ca5e22593eb4212382d60d26350065bf2a02c34b85bc850474a74b589a3de9" +checksum = "8c2cecc5a61c2a053f7f653a24cd15b3b0195d7f7ddb5042c837fb32e161fb7a" dependencies = [ "proc-macro-crate 1.1.0", "proc-macro2",