Compare commits

...

3 Commits

Author SHA1 Message Date
github-actions[bot] 253ae66210 publish new versions (#1761) 2024-09-10 16:14:05 -03:00
Lucas Nogueira 949e2d6c45 chore: update docs 2024-09-10 16:02:38 -03:00
Lucas Fernandes Nogueira 64a6240f79 feat(deep-link): implement getCurrent on Windows/Linux (#1759)
* feat(deep-link): implement getCurrent on Windows/Linux

checks std::env::args() on initialization, also includes integration with the single-instance plugin

* fmt

* update docs, fix event

* add register_all function

* expose api

* be nicer on docs, clippy
2024-09-10 16:00:42 -03:00
22 changed files with 223 additions and 41 deletions
+2 -1
View File
@@ -268,7 +268,8 @@
},
"single-instance": {
"path": "./plugins/single-instance",
"manager": "rust"
"manager": "rust",
"dependencies": ["deep-link"]
},
"sql": {
"path": "./plugins/sql",
@@ -0,0 +1,6 @@
---
"deep-link": patch
"deep-link-js": patch
---
Implement `get_current` on Linux and Windows.
+5
View File
@@ -0,0 +1,5 @@
---
"deep-link": patch
---
Added `register_all` to register all desktop schemes - useful for Linux to not require a formal AppImage installation.
+3
View File
@@ -5,6 +5,8 @@
".changes/barcode-dependencies.md",
".changes/barcode-scanner-validate-plist.md",
".changes/consolidate-permission-state.md",
".changes/deep-link-get-current-desktop.md",
".changes/deep-link-register-all.md",
".changes/dialog-file-response-non-exhaustive.md",
".changes/dialog-return-path.md",
".changes/fix-deep-link-config.md",
@@ -31,6 +33,7 @@
".changes/resolve-content-uris.md",
".changes/shell-open-regex-match-string.md",
".changes/shell-regex-match-string.md",
".changes/single-instance-deep-link.md",
".changes/single-instance-windows-sys.0.59.md",
".changes/sql-uuid-type.md",
".changes/store-remove-mobile-plugin.md",
+5
View File
@@ -0,0 +1,5 @@
---
"single-instance": patch
---
Integrate with the deep link plugin out of the box.
Generated
+4 -2
View File
@@ -1534,6 +1534,7 @@ dependencies = [
"tauri-build",
"tauri-plugin-deep-link",
"tauri-plugin-log",
"tauri-plugin-single-instance",
]
[[package]]
@@ -6496,7 +6497,7 @@ dependencies = [
[[package]]
name = "tauri-plugin-deep-link"
version = "2.0.0-rc.3"
version = "2.0.0-rc.4"
dependencies = [
"dunce",
"log",
@@ -6747,13 +6748,14 @@ dependencies = [
[[package]]
name = "tauri-plugin-single-instance"
version = "2.0.0-rc.1"
version = "2.0.0-rc.2"
dependencies = [
"log",
"semver",
"serde",
"serde_json",
"tauri",
"tauri-plugin-deep-link",
"thiserror",
"windows-sys 0.59.0",
"zbus",
+7
View File
@@ -1,5 +1,9 @@
# Changelog
## \[2.0.0-rc.2]
- [`64a6240f`](https://github.com/tauri-apps/plugins-workspace/commit/64a6240f79fcd52267c8d721b727ae695055d7ff) ([#1759](https://github.com/tauri-apps/plugins-workspace/pull/1759) by [@lucasfernog](https://github.com/tauri-apps/plugins-workspace/../../lucasfernog)) Implement `get_current` on Linux and Windows.
## \[2.0.0-rc.3]
- [`4654591d`](https://github.com/tauri-apps/plugins-workspace/commit/4654591d820403d6fa1a007fd55bb0d85947a6cc) ([#1732](https://github.com/tauri-apps/plugins-workspace/pull/1732) by [@lucasfernog](https://github.com/tauri-apps/plugins-workspace/../../lucasfernog)) Allow empty configuration values.
@@ -104,3 +108,6 @@
- [`eccd6f9`](https://github.com/tauri-apps/plugins-workspace/commit/eccd6f977af7629255b6f5a5205666c9079a86ed)([#504](https://github.com/tauri-apps/plugins-workspace/pull/504)) Initial release.
commit/eccd6f977af7629255b6f5a5205666c9079a86ed)([#504](https://github.com/tauri-apps/plugins-workspace/pull/504)) Initial release.
ithub.com/tauri-apps/plugins-workspace/pull/504)) Initial release.
](https://github.com/tauri-apps/plugins-workspace/commit/eccd6f977af7629255b6f5a5205666c9079a86ed)([#504](https://github.com/tauri-apps/plugins-workspace/pull/504)) Initial release.
commit/eccd6f977af7629255b6f5a5205666c9079a86ed)([#504](https://github.com/tauri-apps/plugins-workspace/pull/504)) Initial release.
ithub.com/tauri-apps/plugins-workspace/pull/504)) Initial release.
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "tauri-plugin-deep-link"
version = "2.0.0-rc.3"
version = "2.0.0-rc.4"
description = "Set your Tauri application as the default handler for an URL"
authors = { workspace = true }
license = { workspace = true }
@@ -21,3 +21,5 @@ dist-ssr
*.njsproj
*.sln
*.sw?
dist/
@@ -1,5 +1,11 @@
# Changelog
## \[2.0.0-rc.1]
### Dependencies
- Upgraded to `deep-link-js@2.0.0-rc.2`
## \[2.0.0-rc.0]
### Dependencies
+2 -2
View File
@@ -1,7 +1,7 @@
{
"name": "deep-link-example",
"private": true,
"version": "2.0.0-rc.0",
"version": "2.0.0-rc.1",
"type": "module",
"scripts": {
"dev": "vite",
@@ -11,7 +11,7 @@
},
"dependencies": {
"@tauri-apps/api": "2.0.0-rc.4",
"@tauri-apps/plugin-deep-link": "2.0.0-rc.1"
"@tauri-apps/plugin-deep-link": "2.0.0-rc.2"
},
"devDependencies": {
"@tauri-apps/cli": "2.0.0-rc.12",
@@ -22,6 +22,7 @@ serde_json = { workspace = true }
tauri = { workspace = true, features = ["wry", "compression"] }
tauri-plugin-deep-link = { path = "../../../" }
tauri-plugin-log = { path = "../../../../log" }
tauri-plugin-single-instance = { path = "../../../../single-instance" }
log = "0.4"
[features]
@@ -13,6 +13,9 @@ fn greet(name: &str) -> String {
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
.plugin(tauri_plugin_single_instance::init(|_app, argv, _cwd| {
println!("single instance triggered: {argv:?}");
}))
.plugin(tauri_plugin_deep_link::init())
.plugin(
tauri_plugin_log::Builder::default()
@@ -20,6 +23,16 @@ pub fn run() {
.build(),
)
.setup(|app| {
// 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;
app.deep_link().register_all()?;
}
app.listen("deep-link://new-url", |url| {
dbg!(url);
});
@@ -29,8 +29,13 @@
},
"deep-link": {
"mobile": [
{ "host": "fabianlars.de", "pathPrefix": ["/intent"] },
{ "host": "tauri.app" }
{
"host": "fabianlars.de",
"pathPrefix": ["/intent"]
},
{
"host": "tauri.app"
}
],
"desktop": {
"schemes": ["fabianlars", "my-tauri-app"]
+3 -1
View File
@@ -14,7 +14,9 @@ import { type UnlistenFn, listen } from '@tauri-apps/api/event'
* const urls = await getCurrent();
* ```
*
* #### - **Windows / Linux**: Unsupported.
* #### - **Windows / Linux**: This function reads the command line arguments and checks if there's only one value, which must be an URL with scheme matching one of the configured values.
* 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..
*
* @since 2.0.0
*/
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@tauri-apps/plugin-deep-link",
"version": "2.0.0-rc.1",
"version": "2.0.0-rc.2",
"description": "Set your Tauri application as the default handler for an URL",
"license": "MIT or APACHE-2.0",
"authors": [
+26 -3
View File
@@ -7,7 +7,7 @@
use serde::{Deserialize, Deserializer};
use tauri_utils::config::DeepLinkProtocol;
#[derive(Deserialize)]
#[derive(Deserialize, Clone)]
pub struct AssociatedDomain {
#[serde(deserialize_with = "deserialize_associated_host")]
pub host: String,
@@ -29,7 +29,7 @@ where
}
}
#[derive(Deserialize)]
#[derive(Deserialize, Clone)]
pub struct Config {
/// Mobile requires `https://<host>` urls.
#[serde(default)]
@@ -41,7 +41,7 @@ pub struct Config {
pub desktop: DesktopProtocol,
}
#[derive(Deserialize)]
#[derive(Deserialize, Clone)]
#[serde(untagged)]
#[allow(unused)] // Used in tauri-bundler
pub enum DesktopProtocol {
@@ -54,3 +54,26 @@ impl Default for DesktopProtocol {
Self::List(Vec::new())
}
}
impl DesktopProtocol {
#[allow(dead_code)]
pub fn contains_scheme(&self, scheme: &String) -> bool {
match self {
Self::One(protocol) => protocol.schemes.contains(scheme),
Self::List(protocols) => protocols
.iter()
.any(|protocol| protocol.schemes.contains(scheme)),
}
}
#[allow(dead_code)]
pub fn schemes(&self) -> Vec<String> {
match self {
Self::One(protocol) => protocol.schemes.clone(),
Self::List(protocols) => protocols
.iter()
.flat_map(|protocol| protocol.schemes.clone())
.collect(),
}
}
}
+97 -13
View File
@@ -2,7 +2,6 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
use serde::de::DeserializeOwned;
use tauri::{
plugin::{Builder, PluginApi, TauriPlugin},
AppHandle, Manager, Runtime,
@@ -17,12 +16,14 @@ pub use error::{Error, Result};
#[cfg(target_os = "android")]
const PLUGIN_IDENTIFIER: &str = "app.tauri.deep_link";
fn init_deep_link<R: Runtime, C: DeserializeOwned>(
fn init_deep_link<R: Runtime>(
app: &AppHandle<R>,
_api: PluginApi<R, C>,
api: PluginApi<R, Option<config::Config>>,
) -> crate::Result<DeepLink<R>> {
#[cfg(target_os = "android")]
{
let _api = api;
use tauri::{
ipc::{Channel, InvokeResponseBody},
Emitter,
@@ -59,11 +60,28 @@ fn init_deep_link<R: Runtime, C: DeserializeOwned>(
return Ok(DeepLink(handle));
}
#[cfg(not(target_os = "android"))]
Ok(DeepLink {
#[cfg(target_os = "ios")]
return Ok(DeepLink {
app: app.clone(),
current: Default::default(),
})
config: api.config().clone(),
});
#[cfg(desktop)]
{
let args = std::env::args();
let current = if let Some(config) = api.config() {
imp::deep_link_from_args(config, args)
} else {
None
};
Ok(DeepLink {
app: app.clone(),
current: std::sync::Mutex::new(current.map(|url| vec![url])),
config: api.config().clone(),
})
}
}
#[cfg(target_os = "android")]
@@ -93,7 +111,9 @@ mod imp {
///
/// ## Platform-specific:
///
/// - **Windows / Linux**: Unsupported, will return [`Error::UnsupportedPlatform`](`crate::Error::UnsupportedPlatform`).
/// - **Windows / Linux**: This function reads the command line arguments and checks if there's only one value, which must be an URL with scheme matching one of the configured values.
/// 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<Option<Vec<url::Url>>> {
self.0
.run_mobile_plugin::<GetCurrentResponse>("getCurrent", ())
@@ -154,23 +174,87 @@ mod imp {
/// Access to the deep-link APIs.
pub struct DeepLink<R: Runtime> {
#[allow(dead_code)]
pub(crate) app: AppHandle<R>,
#[allow(dead_code)]
pub(crate) current: Mutex<Option<Vec<url::Url>>>,
pub(crate) config: Option<crate::config::Config>,
}
pub(crate) fn deep_link_from_args<S: AsRef<str>, I: Iterator<Item = S>>(
config: &crate::config::Config,
mut args: I,
) -> Option<url::Url> {
if cfg!(windows) || cfg!(target_os = "linux") {
args.next(); // bin name
let arg = args.next();
let maybe_deep_link = args.next().is_none(); // single argument
if !maybe_deep_link {
return None;
}
if let Some(url) = arg.and_then(|arg| arg.as_ref().parse::<url::Url>().ok()) {
if config.desktop.contains_scheme(&url.scheme().to_string()) {
return Some(url);
} else if cfg!(debug_assertions) {
log::warn!("argument {url} does not match any configured deep link scheme; skipping it");
}
}
}
None
}
impl<R: Runtime> DeepLink<R> {
/// Checks if the provided list of arguments (which should match [`std::env::args`])
/// contains a deep link argument (for Linux and Windows).
///
/// On Linux and Windows the deep links trigger a new app instance with the deep link URL as its only argument.
///
/// This function does what it can to verify if the argument is actually a deep link, though it could also be a regular CLI argument.
/// To enhance its checks, we only match deep links against the schemes defined in the Tauri configuration
/// i.e. dynamic schemes WON'T be processed.
///
/// This function updates the [`Self::get_current`] value and emits a `deep-link://new-url` event.
#[cfg(desktop)]
pub fn handle_cli_arguments<S: AsRef<str>, I: Iterator<Item = S>>(&self, args: I) {
use tauri::Emitter;
let Some(config) = &self.config else {
return;
};
if let Some(url) = deep_link_from_args(config, args) {
let mut current = self.current.lock().unwrap();
current.replace(vec![url.clone()]);
let _ = self.app.emit("deep-link://new-url", vec![url]);
}
}
/// 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.
///
/// ## Platform-specific:
///
/// - **Windows / Linux**: Unsupported, will return [`Error::UnsupportedPlatform`](`crate::Error::UnsupportedPlatform`).
/// - **Windows / Linux**: This function reads the command line arguments and checks if there's only one value, which must be an URL with scheme matching one of the configured values.
/// 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<Option<Vec<url::Url>>> {
#[cfg(not(any(windows, target_os = "linux")))]
return Ok(self.current.lock().unwrap().clone());
#[cfg(any(windows, target_os = "linux"))]
Err(crate::Error::UnsupportedPlatform)
}
/// Registers all schemes defined in the configuration file.
///
/// This is useful to ensure the schemes are registered even if the user did not install the app properly
/// (e.g. an AppImage that was not properly registered with an AppImage launcher).
pub fn register_all(&self) -> crate::Result<()> {
let Some(config) = &self.config else {
return Ok(());
};
for scheme in config.desktop.schemes() {
self.register(scheme)?;
}
Ok(())
}
/// Register the app as the default handler for the specified protocol.
+8
View File
@@ -1,5 +1,13 @@
# Changelog
## \[2.0.0-rc.2]
- [`64a6240f`](https://github.com/tauri-apps/plugins-workspace/commit/64a6240f79fcd52267c8d721b727ae695055d7ff) ([#1759](https://github.com/tauri-apps/plugins-workspace/pull/1759) by [@lucasfernog](https://github.com/tauri-apps/plugins-workspace/../../lucasfernog)) Integrate with the deep link plugin out of the box.
### Dependencies
- Upgraded to `deep-link@2.0.0-rc.4`
## \[2.0.0-rc.1]
- [`3c52f30e`](https://github.com/tauri-apps/plugins-workspace/commit/3c52f30ea4ec29c51f7021aa7871614d72e43258) ([#1665](https://github.com/tauri-apps/plugins-workspace/pull/1665) by [@amrbashir](https://github.com/tauri-apps/plugins-workspace/../../amrbashir)) Updated `windows-sys` crate to `0.59`
+2 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "tauri-plugin-single-instance"
version = "2.0.0-rc.1"
version = "2.0.0-rc.2"
description = "Ensure a single instance of your tauri app is running."
authors = { workspace = true }
license = { workspace = true }
@@ -19,6 +19,7 @@ serde_json = { workspace = true }
tauri = { workspace = true }
log = { workspace = true }
thiserror = { workspace = true }
tauri-plugin-deep-link = { path = "../deep-link", version = "2.0.0-rc.4" }
semver = { version = "1", optional = true }
[target."cfg(target_os = \"windows\")".dependencies.windows-sys]
+8 -2
View File
@@ -13,6 +13,7 @@
#![cfg(not(any(target_os = "android", target_os = "ios")))]
use tauri::{plugin::TauriPlugin, AppHandle, Manager, Runtime};
use tauri_plugin_deep_link::DeepLink;
#[cfg(target_os = "windows")]
#[path = "platform_impl/windows.rs"]
@@ -31,9 +32,14 @@ pub(crate) type SingleInstanceCallback<R> =
dyn FnMut(&AppHandle<R>, Vec<String>, String) + Send + Sync + 'static;
pub fn init<R: Runtime, F: FnMut(&AppHandle<R>, Vec<String>, String) + Send + Sync + 'static>(
f: F,
mut f: F,
) -> TauriPlugin<R> {
platform_impl::init(Box::new(f))
platform_impl::init(Box::new(move |app, args, cwd| {
if let Some(deep_link) = app.try_state::<DeepLink<R>>() {
deep_link.handle_cli_arguments(args.iter());
}
f(app, args, cwd)
}))
}
pub fn destroy<R: Runtime, M: Manager<R>>(manager: &M) {
+14 -12
View File
@@ -176,7 +176,7 @@ importers:
specifier: 2.0.0-rc.4
version: 2.0.0-rc.4
'@tauri-apps/plugin-deep-link':
specifier: 2.0.0-rc.1
specifier: 2.0.0-rc.2
version: link:../..
devDependencies:
'@tauri-apps/cli':
@@ -2743,9 +2743,9 @@ snapshots:
- encoding
- mocha
'@covector/assemble@0.12.0':
'@covector/assemble@0.12.0(mocha@10.7.3)':
dependencies:
'@covector/command': 0.8.0
'@covector/command': 0.8.0(mocha@10.7.3)
'@covector/files': 0.8.0
effection: 2.0.8(mocha@10.7.3)
js-yaml: 4.1.0
@@ -2756,9 +2756,10 @@ snapshots:
unified: 9.2.2
transitivePeerDependencies:
- encoding
- mocha
- supports-color
'@covector/changelog@0.12.0':
'@covector/changelog@0.12.0(mocha@10.7.3)':
dependencies:
'@covector/files': 0.8.0
effection: 2.0.8(mocha@10.7.3)
@@ -2768,14 +2769,16 @@ snapshots:
unified: 9.2.2
transitivePeerDependencies:
- encoding
- mocha
- supports-color
'@covector/command@0.8.0':
'@covector/command@0.8.0(mocha@10.7.3)':
dependencies:
'@effection/process': 2.1.4
'@effection/process': 2.1.4(mocha@10.7.3)
effection: 2.0.8(mocha@10.7.3)
transitivePeerDependencies:
- encoding
- mocha
'@covector/files@0.8.0':
dependencies:
@@ -2822,10 +2825,8 @@ snapshots:
dependencies:
effection: 2.0.8(mocha@10.7.3)
mocha: 10.7.3
transitivePeerDependencies:
- encoding
'@effection/process@2.1.4':
'@effection/process@2.1.4(mocha@10.7.3)':
dependencies:
cross-spawn: 7.0.3
ctrlc-windows: 2.1.0
@@ -2833,6 +2834,7 @@ snapshots:
shellwords: 0.1.1
transitivePeerDependencies:
- encoding
- mocha
'@effection/stream@2.0.6':
dependencies:
@@ -3678,9 +3680,9 @@ snapshots:
dependencies:
'@clack/prompts': 0.7.0
'@covector/apply': 0.10.0(mocha@10.7.3)
'@covector/assemble': 0.12.0
'@covector/changelog': 0.12.0
'@covector/command': 0.8.0
'@covector/assemble': 0.12.0(mocha@10.7.3)
'@covector/changelog': 0.12.0(mocha@10.7.3)
'@covector/command': 0.8.0(mocha@10.7.3)
'@covector/files': 0.8.0
effection: 2.0.8(mocha@10.7.3)
globby: 11.1.0