diff --git a/.changes/deep-link-on-new-url.md b/.changes/deep-link-on-new-url.md new file mode 100644 index 00000000..b37bed90 --- /dev/null +++ b/.changes/deep-link-on-new-url.md @@ -0,0 +1,6 @@ +--- +"deep-link": patch +--- + +Added `DeepLink::on_open_url` function to match the JavaScript API implementation, +which wraps the `deep-link://new-url` event and also send the current deep link if there's any. diff --git a/plugins/deep-link/examples/app/src-tauri/src/lib.rs b/plugins/deep-link/examples/app/src-tauri/src/lib.rs index eba3f679..f85527d9 100644 --- a/plugins/deep-link/examples/app/src-tauri/src/lib.rs +++ b/plugins/deep-link/examples/app/src-tauri/src/lib.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT -use tauri::Listener; +use tauri_plugin_deep_link::DeepLinkExt; // Learn more about Tauri commands at https://tauri.app/v1/guides/features/command #[tauri::command] @@ -33,16 +33,14 @@ pub fn run() { // ensure deep links are registered on the system // this is useful because AppImages requires additional setup to be available in the system // and calling register() makes the deep links immediately available - without any user input - #[cfg(target_os = "linux")] - { - use tauri_plugin_deep_link::DeepLinkExt; + // additionally, we manually register on Windows on debug builds for development + #[cfg(any(target_os = "linux", all(debug_assertions, windows)))] + app.deep_link().register_all()?; - app.deep_link().register_all()?; - } - - app.listen("deep-link://new-url", |url| { - dbg!(url); + app.deep_link().on_open_url(|event| { + dbg!(event.urls()); }); + Ok(()) }) .invoke_handler(tauri::generate_handler![greet]) diff --git a/plugins/deep-link/src/lib.rs b/plugins/deep-link/src/lib.rs index 1fff4e44..ea265504 100644 --- a/plugins/deep-link/src/lib.rs +++ b/plugins/deep-link/src/lib.rs @@ -2,9 +2,11 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT +use std::sync::Arc; + use tauri::{ plugin::{Builder, PluginApi, TauriPlugin}, - AppHandle, Manager, Runtime, + AppHandle, EventId, Listener, Manager, Runtime, }; mod commands; @@ -57,7 +59,10 @@ fn init_deep_link( }, )?; - return Ok(DeepLink(handle)); + return Ok(DeepLink { + app: app.clone(), + plugin_handle: handle, + }); } #[cfg(target_os = "ios")] @@ -83,10 +88,9 @@ fn init_deep_link( #[cfg(target_os = "android")] mod imp { - use tauri::{plugin::PluginHandle, Runtime}; + use tauri::{ipc::Channel, plugin::PluginHandle, AppHandle, Runtime}; use serde::{Deserialize, Serialize}; - use tauri::ipc::Channel; #[derive(Serialize)] #[serde(rename_all = "camelCase")] @@ -101,7 +105,10 @@ mod imp { } /// Access to the deep-link APIs. - pub struct DeepLink(pub(crate) PluginHandle); + pub struct DeepLink { + pub(crate) app: AppHandle, + pub(crate) plugin_handle: PluginHandle, + } impl DeepLink { /// Get the current URLs that triggered the deep link. Use this on app load to check whether your app was started via a deep link. @@ -112,7 +119,7 @@ mod imp { /// Note that you must manually check the arguments when registering deep link schemes dynamically with [`Self::register`]. /// Additionally, the deep link might have been provided as a CLI argument so you should check if its format matches what you expect. pub fn get_current(&self) -> crate::Result>> { - self.0 + self.plugin_handle .run_mobile_plugin::("getCurrent", ()) .map(|v| v.url.map(|url| vec![url])) .map_err(Into::into) @@ -437,6 +444,7 @@ mod imp { } pub use imp::DeepLink; +use url::Url; /// Extensions to [`tauri::App`], [`tauri::AppHandle`], [`tauri::WebviewWindow`], [`tauri::Webview`] and [`tauri::Window`] to access the deep-link APIs. pub trait DeepLinkExt { @@ -449,6 +457,54 @@ impl> crate::DeepLinkExt for T { } } +/// Event that is triggered when the app was requested to open a new URL. +/// +/// Typed [`tauri::Event`]. +pub struct OpenUrlEvent { + id: EventId, + urls: Vec, +} + +impl OpenUrlEvent { + /// The event ID which can be used to stop listening to the event via [`tauri::Listener::unlisten`]. + pub fn id(&self) -> EventId { + self.id + } + + /// The event URLs. + pub fn urls(self) -> Vec { + self.urls + } +} + +impl DeepLink { + /// Handle a new deep link being triggered to open the app. + /// + /// To avoid race conditions, if the app was started with a deep link, + /// the closure gets immediately called with the deep link URL. + pub fn on_open_url(&self, f: F) -> EventId { + let f = Arc::new(f); + let f_ = f.clone(); + let event_id = self.app.listen("deep-link://new-url", move |event| { + if let Ok(urls) = serde_json::from_str(event.payload()) { + f(OpenUrlEvent { + id: event.id(), + urls, + }) + } + }); + + if let Ok(Some(current)) = self.get_current() { + f_(OpenUrlEvent { + id: event_id, + urls: current, + }) + } + + event_id + } +} + /// Initializes the plugin. pub fn init() -> TauriPlugin> { Builder::new("deep-link")