mirror of
https://github.com/tauri-apps/plugins-workspace.git
synced 2026-09-26 21:41:48 +02:00
feat(deep-link): Add deep link support for desktop (#916)
This commit is contained in:
@@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
"deep-link": patch
|
||||||
|
"deep-link-js": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Added desktop support.
|
||||||
@@ -56,6 +56,9 @@ jobs:
|
|||||||
tauri-plugin-clipboard-manager:
|
tauri-plugin-clipboard-manager:
|
||||||
- .github/workflows/test-rust.yml
|
- .github/workflows/test-rust.yml
|
||||||
- plugins/clipboard-manager/**
|
- plugins/clipboard-manager/**
|
||||||
|
tauri-plugin-deep-link:
|
||||||
|
- .github/workflows/test-rust.yml
|
||||||
|
- plugins/deep-link/**
|
||||||
tauri-plugin-dialog:
|
tauri-plugin-dialog:
|
||||||
- .github/workflows/test-rust.yml
|
- .github/workflows/test-rust.yml
|
||||||
- plugins/dialog/**
|
- plugins/dialog/**
|
||||||
|
|||||||
Generated
+81
-1
@@ -1161,6 +1161,26 @@ version = "0.9.6"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
|
checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "const-random"
|
||||||
|
version = "0.1.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359"
|
||||||
|
dependencies = [
|
||||||
|
"const-random-macro",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "const-random-macro"
|
||||||
|
version = "0.1.16"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom 0.2.12",
|
||||||
|
"once_cell",
|
||||||
|
"tiny-keccak",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "constant_time_eq"
|
name = "constant_time_eq"
|
||||||
version = "0.1.5"
|
version = "0.1.5"
|
||||||
@@ -1621,6 +1641,15 @@ dependencies = [
|
|||||||
"syn 2.0.52",
|
"syn 2.0.52",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dlv-list"
|
||||||
|
version = "0.5.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "442039f5147480ba31067cb00ada1adae6892028e40e45fc5de7b7df6dcc1b5f"
|
||||||
|
dependencies = [
|
||||||
|
"const-random",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dotenvy"
|
name = "dotenvy"
|
||||||
version = "0.15.7"
|
version = "0.15.7"
|
||||||
@@ -3199,7 +3228,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19"
|
checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"windows-targets 0.48.5",
|
"windows-targets 0.52.5",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -3830,6 +3859,16 @@ dependencies = [
|
|||||||
"vcpkg",
|
"vcpkg",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ordered-multimap"
|
||||||
|
version = "0.7.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "49203cdcae0030493bad186b28da2fa25645fa276a51b6fec8010d281e02ef79"
|
||||||
|
dependencies = [
|
||||||
|
"dlv-list",
|
||||||
|
"hashbrown 0.14.3",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ordered-stream"
|
name = "ordered-stream"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
@@ -4779,6 +4818,17 @@ dependencies = [
|
|||||||
"crossbeam-utils",
|
"crossbeam-utils",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rust-ini"
|
||||||
|
version = "0.21.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0d625ed57d8f49af6cfa514c42e1a71fadcff60eb0b1c517ff82fe41aa025b41"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"ordered-multimap",
|
||||||
|
"trim-in-place",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rust_decimal"
|
name = "rust_decimal"
|
||||||
version = "1.34.3"
|
version = "1.34.3"
|
||||||
@@ -6132,13 +6182,18 @@ dependencies = [
|
|||||||
name = "tauri-plugin-deep-link"
|
name = "tauri-plugin-deep-link"
|
||||||
version = "2.0.0-beta.4"
|
version = "2.0.0-beta.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"dunce",
|
||||||
"log",
|
"log",
|
||||||
|
"rust-ini",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"tauri",
|
"tauri",
|
||||||
"tauri-plugin",
|
"tauri-plugin",
|
||||||
|
"tauri-utils",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"url",
|
"url",
|
||||||
|
"windows-registry",
|
||||||
|
"windows-result",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -6702,6 +6757,15 @@ dependencies = [
|
|||||||
"time-core",
|
"time-core",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tiny-keccak"
|
||||||
|
version = "2.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237"
|
||||||
|
dependencies = [
|
||||||
|
"crunchy",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tiny_http"
|
name = "tiny_http"
|
||||||
version = "0.12.0"
|
version = "0.12.0"
|
||||||
@@ -7024,6 +7088,12 @@ dependencies = [
|
|||||||
"serde_json",
|
"serde_json",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "trim-in-place"
|
||||||
|
version = "0.1.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "343e926fc669bc8cde4fa3129ab681c63671bae288b1f1081ceee6d9d37904fc"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "try-lock"
|
name = "try-lock"
|
||||||
version = "0.2.5"
|
version = "0.2.5"
|
||||||
@@ -7742,6 +7812,16 @@ dependencies = [
|
|||||||
"syn 2.0.52",
|
"syn 2.0.52",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-registry"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f721bc2e55efb506a1a395a545cb76c2481fb023d33b51f0050e7888716281cf"
|
||||||
|
dependencies = [
|
||||||
|
"windows-result",
|
||||||
|
"windows-targets 0.52.5",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-result"
|
name = "windows-result"
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ log = "0.4"
|
|||||||
tauri = "2.0.0-beta.17"
|
tauri = "2.0.0-beta.17"
|
||||||
tauri-build = "2.0.0-beta.13"
|
tauri-build = "2.0.0-beta.13"
|
||||||
tauri-plugin = "2.0.0-beta.13"
|
tauri-plugin = "2.0.0-beta.13"
|
||||||
|
tauri-utils = "2.0.0-beta.13"
|
||||||
serde_json = "1"
|
serde_json = "1"
|
||||||
thiserror = "1"
|
thiserror = "1"
|
||||||
url = "2"
|
url = "2"
|
||||||
|
|||||||
@@ -17,12 +17,22 @@ targets = [ "x86_64-linux-android" ]
|
|||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
serde = { workspace = true }
|
serde = { workspace = true }
|
||||||
serde_json = { workspace = true }
|
serde_json = { workspace = true }
|
||||||
|
tauri-utils = { workspace = true }
|
||||||
tauri-plugin = { workspace = true, features = [ "build" ] }
|
tauri-plugin = { workspace = true, features = [ "build" ] }
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
serde = { workspace = true }
|
serde = { workspace = true }
|
||||||
serde_json = { workspace = true }
|
serde_json = { workspace = true }
|
||||||
tauri = { workspace = true }
|
tauri = { workspace = true }
|
||||||
|
tauri-utils = { workspace = true }
|
||||||
log = { workspace = true }
|
log = { workspace = true }
|
||||||
thiserror = { workspace = true }
|
thiserror = { workspace = true }
|
||||||
url = { workspace = true }
|
url = { workspace = true }
|
||||||
|
|
||||||
|
[target."cfg(windows)".dependencies]
|
||||||
|
dunce = "1"
|
||||||
|
windows-registry = "0.1"
|
||||||
|
windows-result = "0.1"
|
||||||
|
|
||||||
|
[target.'cfg(target_os = "linux")'.dependencies]
|
||||||
|
rust-ini = "0.21"
|
||||||
|
|||||||
@@ -93,16 +93,19 @@ See [supporting associated domains](https://developer.apple.com/documentation/xc
|
|||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
Under `tauri.conf.json > plugins > deep-link`, configure the domains you want to associate with your application:
|
Under `tauri.conf.json > plugins > deep-link`, configure the domains (mobile) and schemes (desktop) you want to associate with your application:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"plugins": {
|
"plugins": {
|
||||||
"deep-link": {
|
"deep-link": {
|
||||||
"domains": [
|
"mobile": [
|
||||||
{ "host": "your.website.com", "pathPrefix": ["/open"] },
|
{ "host": "your.website.com", "pathPrefix": ["/open"] },
|
||||||
{ "host": "another.site.br" }
|
{ "host": "another.site.br" }
|
||||||
]
|
],
|
||||||
|
"desktop": {
|
||||||
|
"schemes": ["something", "my-tauri-app"]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -128,10 +131,12 @@ Afterwards all the plugin's APIs are available through the JavaScript guest bind
|
|||||||
```javascript
|
```javascript
|
||||||
import { onOpenUrl } from "@tauri-apps/plugin-deep-link";
|
import { onOpenUrl } from "@tauri-apps/plugin-deep-link";
|
||||||
await onOpenUrl((urls) => {
|
await onOpenUrl((urls) => {
|
||||||
console.log('deep link:', urls);
|
console.log("deep link:", urls);
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Note that the Plugin will only emit events on macOS, iOS and Android. On Windows and Linux the OS will spawn a new instance of your app with the URL as a CLI argument. If you want your app to behave on Windows & Linux similar to the other platforms you can use the [single-instance](../single-instance/) plugin.
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
PRs accepted. Please make sure to read the Contributing Guide before making a pull request.
|
PRs accepted. Please make sure to read the Contributing Guide before making a pull request.
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
if("__TAURI__"in window){var __TAURI_PLUGIN_DEEPLINK__=function(e){"use strict";function n(e,n=!1){return window.__TAURI_INTERNALS__.transformCallback(e,n)}async function r(e,n={},r){return window.__TAURI_INTERNALS__.invoke(e,n,r)}var t;async function i(e,t,i){const _=(void 0,{kind:"Any"});return r("plugin:event|listen",{event:e,target:_,handler:n(t)}).then((n=>async()=>async function(e,n){await r("plugin:event|unlisten",{event:e,eventId:n})}(e,n)))}async function _(){return await r("plugin:deep-link|get_current")}return"function"==typeof SuppressedError&&SuppressedError,function(e){e.WINDOW_RESIZED="tauri://resize",e.WINDOW_MOVED="tauri://move",e.WINDOW_CLOSE_REQUESTED="tauri://close-requested",e.WINDOW_DESTROYED="tauri://destroyed",e.WINDOW_FOCUS="tauri://focus",e.WINDOW_BLUR="tauri://blur",e.WINDOW_SCALE_FACTOR_CHANGED="tauri://scale-change",e.WINDOW_THEME_CHANGED="tauri://theme-changed",e.WINDOW_CREATED="tauri://window-created",e.WEBVIEW_CREATED="tauri://webview-created",e.DRAG="tauri://drag",e.DROP="tauri://drop",e.DROP_OVER="tauri://drop-over",e.DROP_CANCELLED="tauri://drag-cancelled"}(t||(t={})),e.getCurrent=_,e.onOpenUrl=async function(e){const n=await _();return null!=n&&e(n),await i("deep-link://new-url",(n=>{e(n.payload)}))},e}({});Object.defineProperty(window.__TAURI__,"deepLink",{value:__TAURI_PLUGIN_DEEPLINK__})}
|
if("__TAURI__"in window){var __TAURI_PLUGIN_DEEPLINK__=function(e){"use strict";function n(e,n=!1){return window.__TAURI_INTERNALS__.transformCallback(e,n)}async function r(e,n={},r){return window.__TAURI_INTERNALS__.invoke(e,n,r)}var t;async function i(e,t,i){const a=(void 0,{kind:"Any"});return r("plugin:event|listen",{event:e,target:a,handler:n(t)}).then((n=>async()=>async function(e,n){await r("plugin:event|unlisten",{event:e,eventId:n})}(e,n)))}async function a(){return await r("plugin:deep-link|get_current")}return"function"==typeof SuppressedError&&SuppressedError,function(e){e.WINDOW_RESIZED="tauri://resize",e.WINDOW_MOVED="tauri://move",e.WINDOW_CLOSE_REQUESTED="tauri://close-requested",e.WINDOW_DESTROYED="tauri://destroyed",e.WINDOW_FOCUS="tauri://focus",e.WINDOW_BLUR="tauri://blur",e.WINDOW_SCALE_FACTOR_CHANGED="tauri://scale-change",e.WINDOW_THEME_CHANGED="tauri://theme-changed",e.WINDOW_CREATED="tauri://window-created",e.WEBVIEW_CREATED="tauri://webview-created",e.DRAG="tauri://drag",e.DROP="tauri://drop",e.DROP_OVER="tauri://drop-over",e.DROP_CANCELLED="tauri://drag-cancelled"}(t||(t={})),e.getCurrent=a,e.isRegistered=async function(e){return await r("plugin:deep-link|i_registered",{protocol:e})},e.onOpenUrl=async function(e){const n=await a();return null!=n&&e(n),await i("deep-link://new-url",(n=>{e(n.payload)}))},e.register=async function(e){return await r("plugin:deep-link|register",{protocol:e})},e.unregister=async function(e){return await r("plugin:deep-link|unregister",{protocol:e})},e}({});Object.defineProperty(window.__TAURI__,"deepLink",{value:__TAURI_PLUGIN_DEEPLINK__})}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
mod config;
|
mod config;
|
||||||
use config::{AssociatedDomain, Config};
|
use config::{AssociatedDomain, Config};
|
||||||
|
|
||||||
const COMMANDS: &[&str] = &["get_current"];
|
const COMMANDS: &[&str] = &["get_current", "register", "unregister", "is_registered"];
|
||||||
|
|
||||||
// TODO: Consider using activity-alias in case users may have multiple activities in their app.
|
// TODO: Consider using activity-alias in case users may have multiple activities in their app.
|
||||||
// TODO: Do we want to support the other path* configs too?
|
// TODO: Do we want to support the other path* configs too?
|
||||||
@@ -48,7 +48,7 @@ fn main() {
|
|||||||
"DEEP LINK PLUGIN",
|
"DEEP LINK PLUGIN",
|
||||||
"activity",
|
"activity",
|
||||||
config
|
config
|
||||||
.domains
|
.mobile
|
||||||
.iter()
|
.iter()
|
||||||
.map(intent_filter)
|
.map(intent_filter)
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
@@ -62,7 +62,7 @@ fn main() {
|
|||||||
entitlements.insert(
|
entitlements.insert(
|
||||||
"com.apple.developer.associated-domains".into(),
|
"com.apple.developer.associated-domains".into(),
|
||||||
config
|
config
|
||||||
.domains
|
.mobile
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|d| format!("applinks:{}", d.host).into())
|
.map(|d| format!("applinks:{}", d.host).into())
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
|
|||||||
@@ -28,15 +28,13 @@
|
|||||||
"hello": "world"
|
"hello": "world"
|
||||||
},
|
},
|
||||||
"deep-link": {
|
"deep-link": {
|
||||||
"domains": [
|
"mobile": [
|
||||||
{
|
{ "host": "fabianlars.de", "pathPrefix": ["/intent"] },
|
||||||
"host": "fabianlars.de",
|
{ "host": "tauri.app" }
|
||||||
"pathPrefix": ["/intent"]
|
],
|
||||||
},
|
"desktop": {
|
||||||
{
|
"schemes": ["fabianlars", "my-tauri-app"]
|
||||||
"host": "tauri.app"
|
}
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"bundle": {
|
"bundle": {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
"target": "ESNext",
|
"target": "ESNext",
|
||||||
"module": "ESNext",
|
"module": "ESNext",
|
||||||
"lib": ["ESNext", "DOM"],
|
"lib": ["ESNext", "DOM"],
|
||||||
"moduleResolution": "Node",
|
"moduleResolution": "bundler",
|
||||||
"strict": true,
|
"strict": true,
|
||||||
"sourceMap": true,
|
"sourceMap": true,
|
||||||
"resolveJsonModule": true,
|
"resolveJsonModule": true,
|
||||||
|
|||||||
@@ -5,12 +5,95 @@
|
|||||||
import { invoke } from "@tauri-apps/api/core";
|
import { invoke } from "@tauri-apps/api/core";
|
||||||
import { type UnlistenFn, listen } from "@tauri-apps/api/event";
|
import { type UnlistenFn, listen } from "@tauri-apps/api/event";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```typescript
|
||||||
|
* import { getCurrent } from '@tauri-apps/plugin-deep-link';
|
||||||
|
* const urls = await getCurrent();
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* #### - **Windows / Linux**: Unsupported.
|
||||||
|
*
|
||||||
|
* @since 2.0.0
|
||||||
|
*/
|
||||||
export async function getCurrent(): Promise<string[] | null> {
|
export async function getCurrent(): Promise<string[] | null> {
|
||||||
return await invoke<string[] | null>("plugin:deep-link|get_current");
|
return await invoke("plugin:deep-link|get_current");
|
||||||
|
|
||||||
// return await invoke("plugin:deep-link|get_current");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register the app as the default handler for the specified protocol.
|
||||||
|
*
|
||||||
|
* @param protocol The name of the protocol without `://`. For example, if you want your app to handle `tauri://` links, call this method with `tauri` as the protocol.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```typescript
|
||||||
|
* import { register } from '@tauri-apps/plugin-deep-link';
|
||||||
|
* await register("my-scheme");
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* #### - **macOS / Android / iOS**: Unsupported.
|
||||||
|
*
|
||||||
|
* @since 2.0.0
|
||||||
|
*/
|
||||||
|
export async function register(protocol: string): Promise<null> {
|
||||||
|
return await invoke("plugin:deep-link|register", { protocol });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unregister the app as the default handler for the specified protocol.
|
||||||
|
*
|
||||||
|
* @param protocol The name of the protocol without `://`.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```typescript
|
||||||
|
* import { unregister } from '@tauri-apps/plugin-deep-link';
|
||||||
|
* await unregister("my-scheme");
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* #### - **macOS / Linux / Android / iOS**: Unsupported.
|
||||||
|
*
|
||||||
|
* @since 2.0.0
|
||||||
|
*/
|
||||||
|
export async function unregister(protocol: string): Promise<null> {
|
||||||
|
return await invoke("plugin:deep-link|unregister", { protocol });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether the app is the default handler for the specified protocol.
|
||||||
|
*
|
||||||
|
* @param protocol The name of the protocol without `://`.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```typescript
|
||||||
|
* import { isRegistered } from '@tauri-apps/plugin-deep-link';
|
||||||
|
* await isRegistered("my-scheme");
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* #### - **macOS / Android / iOS**: Unsupported, always returns `true`.
|
||||||
|
*
|
||||||
|
* @since 2.0.0
|
||||||
|
*/
|
||||||
|
export async function isRegistered(protocol: string): Promise<boolean> {
|
||||||
|
return await invoke("plugin:deep-link|i_registered", { protocol });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function for the `deep-link://new-url` event to run a function each time the protocol is triggered while the app is running. Use `getCurrent` on app load to check whether your app was started via a deep link.
|
||||||
|
*
|
||||||
|
* @param protocol The name of the protocol without `://`.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```typescript
|
||||||
|
* import { onOpenUrl } from '@tauri-apps/plugin-deep-link';
|
||||||
|
* await onOpenUrl((urls) => { console.log(urls) });
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* #### - **Windows / Linux**: Unsupported, the OS will spawn a new app instance passing the URL as a CLI argument.
|
||||||
|
*
|
||||||
|
* @since 2.0.0
|
||||||
|
*/
|
||||||
export async function onOpenUrl(
|
export async function onOpenUrl(
|
||||||
handler: (urls: string[]) => void,
|
handler: (urls: string[]) => void,
|
||||||
): Promise<UnlistenFn> {
|
): Promise<UnlistenFn> {
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
# Automatically generated - DO NOT EDIT!
|
||||||
|
|
||||||
|
"$schema" = "../../schemas/schema.json"
|
||||||
|
|
||||||
|
[[permission]]
|
||||||
|
identifier = "allow-is-registered"
|
||||||
|
description = "Enables the is_registered command without any pre-configured scope."
|
||||||
|
commands.allow = ["is_registered"]
|
||||||
|
|
||||||
|
[[permission]]
|
||||||
|
identifier = "deny-is-registered"
|
||||||
|
description = "Denies the is_registered command without any pre-configured scope."
|
||||||
|
commands.deny = ["is_registered"]
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
# Automatically generated - DO NOT EDIT!
|
||||||
|
|
||||||
|
"$schema" = "../../schemas/schema.json"
|
||||||
|
|
||||||
|
[[permission]]
|
||||||
|
identifier = "allow-register"
|
||||||
|
description = "Enables the register command without any pre-configured scope."
|
||||||
|
commands.allow = ["register"]
|
||||||
|
|
||||||
|
[[permission]]
|
||||||
|
identifier = "deny-register"
|
||||||
|
description = "Denies the register command without any pre-configured scope."
|
||||||
|
commands.deny = ["register"]
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
# Automatically generated - DO NOT EDIT!
|
||||||
|
|
||||||
|
"$schema" = "../../schemas/schema.json"
|
||||||
|
|
||||||
|
[[permission]]
|
||||||
|
identifier = "allow-unregister"
|
||||||
|
description = "Enables the unregister command without any pre-configured scope."
|
||||||
|
commands.allow = ["unregister"]
|
||||||
|
|
||||||
|
[[permission]]
|
||||||
|
identifier = "deny-unregister"
|
||||||
|
description = "Denies the unregister command without any pre-configured scope."
|
||||||
|
commands.deny = ["unregister"]
|
||||||
@@ -2,4 +2,10 @@
|
|||||||
|------|-----|
|
|------|-----|
|
||||||
|`allow-get-current`|Enables the get_current command without any pre-configured scope.|
|
|`allow-get-current`|Enables the get_current command without any pre-configured scope.|
|
||||||
|`deny-get-current`|Denies the get_current command without any pre-configured scope.|
|
|`deny-get-current`|Denies the get_current command without any pre-configured scope.|
|
||||||
|
|`allow-is-registered`|Enables the is_registered command without any pre-configured scope.|
|
||||||
|
|`deny-is-registered`|Denies the is_registered command without any pre-configured scope.|
|
||||||
|
|`allow-register`|Enables the register command without any pre-configured scope.|
|
||||||
|
|`deny-register`|Denies the register command without any pre-configured scope.|
|
||||||
|
|`allow-unregister`|Enables the unregister command without any pre-configured scope.|
|
||||||
|
|`deny-unregister`|Denies the unregister command without any pre-configured scope.|
|
||||||
|`default`|Allows reading the opened deep link via the get_current command|
|
|`default`|Allows reading the opened deep link via the get_current command|
|
||||||
|
|||||||
@@ -308,6 +308,48 @@
|
|||||||
"deny-get-current"
|
"deny-get-current"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"description": "allow-is-registered -> Enables the is_registered command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"allow-is-registered"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "deny-is-registered -> Denies the is_registered command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"deny-is-registered"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "allow-register -> Enables the register command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"allow-register"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "deny-register -> Denies the register command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"deny-register"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "allow-unregister -> Enables the unregister command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"allow-unregister"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "deny-unregister -> Denies the unregister command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"deny-unregister"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"description": "default -> Allows reading the opened deep link via the get_current command",
|
"description": "default -> Allows reading the opened deep link via the get_current command",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
|
|||||||
@@ -14,3 +14,33 @@ pub(crate) async fn get_current<R: Runtime>(
|
|||||||
) -> Result<Option<Vec<url::Url>>> {
|
) -> Result<Option<Vec<url::Url>>> {
|
||||||
deep_link.get_current()
|
deep_link.get_current()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[command]
|
||||||
|
pub(crate) async fn register<R: Runtime>(
|
||||||
|
_app: AppHandle<R>,
|
||||||
|
_window: Window<R>,
|
||||||
|
deep_link: State<'_, DeepLink<R>>,
|
||||||
|
protocol: String,
|
||||||
|
) -> Result<()> {
|
||||||
|
deep_link.register(protocol)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[command]
|
||||||
|
pub(crate) async fn unregister<R: Runtime>(
|
||||||
|
_app: AppHandle<R>,
|
||||||
|
_window: Window<R>,
|
||||||
|
deep_link: State<'_, DeepLink<R>>,
|
||||||
|
protocol: String,
|
||||||
|
) -> Result<()> {
|
||||||
|
deep_link.unregister(protocol)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[command]
|
||||||
|
pub(crate) async fn is_registered<R: Runtime>(
|
||||||
|
_app: AppHandle<R>,
|
||||||
|
_window: Window<R>,
|
||||||
|
deep_link: State<'_, DeepLink<R>>,
|
||||||
|
protocol: String,
|
||||||
|
) -> Result<bool> {
|
||||||
|
deep_link.is_registered(protocol)
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,9 +4,8 @@
|
|||||||
|
|
||||||
// This module is also imported in build.rs!
|
// This module is also imported in build.rs!
|
||||||
|
|
||||||
#![allow(dead_code)]
|
|
||||||
|
|
||||||
use serde::{Deserialize, Deserializer};
|
use serde::{Deserialize, Deserializer};
|
||||||
|
use tauri_utils::config::DeepLinkProtocol;
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
pub struct AssociatedDomain {
|
pub struct AssociatedDomain {
|
||||||
@@ -32,5 +31,16 @@ where
|
|||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
pub domains: Vec<AssociatedDomain>,
|
/// Mobile requires `https://<host>` urls.
|
||||||
|
pub mobile: Vec<AssociatedDomain>,
|
||||||
|
/// Desktop requires urls starting with `<scheme>://`.
|
||||||
|
/// These urls are also active in dev mode on Android.
|
||||||
|
pub desktop: DesktopProtocol,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
pub enum DesktopProtocol {
|
||||||
|
One(DeepLinkProtocol),
|
||||||
|
List(Vec<DeepLinkProtocol>),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,8 +8,21 @@ pub type Result<T> = std::result::Result<T, Error>;
|
|||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
|
#[error("unsupported platform")]
|
||||||
|
UnsupportedPlatform,
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Io(#[from] std::io::Error),
|
Io(#[from] std::io::Error),
|
||||||
|
#[error(transparent)]
|
||||||
|
Tauri(#[from] tauri::Error),
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
#[error(transparent)]
|
||||||
|
Windows(#[from] windows_result::Error),
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
#[error(transparent)]
|
||||||
|
Ini(#[from] ini::Error),
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
#[error(transparent)]
|
||||||
|
ParseIni(#[from] ini::ParseError),
|
||||||
#[cfg(mobile)]
|
#[cfg(mobile)]
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
PluginInvoke(#[from] tauri::plugin::mobile::PluginInvokeError),
|
PluginInvoke(#[from] tauri::plugin::mobile::PluginInvokeError),
|
||||||
|
|||||||
@@ -82,32 +82,279 @@ mod imp {
|
|||||||
pub struct DeepLink<R: Runtime>(pub(crate) PluginHandle<R>);
|
pub struct DeepLink<R: Runtime>(pub(crate) PluginHandle<R>);
|
||||||
|
|
||||||
impl<R: Runtime> DeepLink<R> {
|
impl<R: Runtime> DeepLink<R> {
|
||||||
/// Get the current URLs that triggered the deep link.
|
/// 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`).
|
||||||
pub fn get_current(&self) -> crate::Result<Option<Vec<url::Url>>> {
|
pub fn get_current(&self) -> crate::Result<Option<Vec<url::Url>>> {
|
||||||
self.0
|
self.0
|
||||||
.run_mobile_plugin::<GetCurrentResponse>("getCurrent", ())
|
.run_mobile_plugin::<GetCurrentResponse>("getCurrent", ())
|
||||||
.map(|v| v.url.map(|url| vec![url]))
|
.map(|v| v.url.map(|url| vec![url]))
|
||||||
.map_err(Into::into)
|
.map_err(Into::into)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Register the app as the default handler for the specified protocol.
|
||||||
|
///
|
||||||
|
/// - `protocol`: The name of the protocol without `://`. For example, if you want your app to handle `tauri://` links, call this method with `tauri` as the protocol.
|
||||||
|
///
|
||||||
|
/// ## Platform-specific:
|
||||||
|
///
|
||||||
|
/// - **macOS / Android / iOS**: Unsupported, will return [`Error::UnsupportedPlatform`](`crate::Error::UnsupportedPlatform`).
|
||||||
|
pub fn register<S: AsRef<str>>(&self, _protocol: S) -> crate::Result<()> {
|
||||||
|
Err(crate::Error::UnsupportedPlatform)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Unregister the app as the default handler for the specified protocol.
|
||||||
|
///
|
||||||
|
/// - `protocol`: The name of the protocol without `://`.
|
||||||
|
///
|
||||||
|
/// ## Platform-specific:
|
||||||
|
///
|
||||||
|
/// - **Linux**: Can only unregister the scheme if it was initially registered with [`register`](`Self::register`). May not work on older distros.
|
||||||
|
/// - **macOS / Android / iOS**: Unsupported, will return [`Error::UnsupportedPlatform`](`crate::Error::UnsupportedPlatform`).
|
||||||
|
pub fn unregister<S: AsRef<str>>(&self, _protocol: S) -> crate::Result<()> {
|
||||||
|
Err(crate::Error::UnsupportedPlatform)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check whether the app is the default handler for the specified protocol.
|
||||||
|
///
|
||||||
|
/// - `protocol`: The name of the protocol without `://`.
|
||||||
|
///
|
||||||
|
/// ## Platform-specific:
|
||||||
|
///
|
||||||
|
/// - **macOS / Android / iOS**: Unsupported, will return [`Error::UnsupportedPlatform`](`crate::Error::UnsupportedPlatform`).
|
||||||
|
pub fn is_registered<S: AsRef<str>>(&self, _protocol: S) -> crate::Result<bool> {
|
||||||
|
Err(crate::Error::UnsupportedPlatform)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(target_os = "android"))]
|
#[cfg(not(target_os = "android"))]
|
||||||
mod imp {
|
mod imp {
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
use std::{
|
||||||
|
fs::{create_dir_all, File},
|
||||||
|
io::Write,
|
||||||
|
process::Command,
|
||||||
|
};
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
use tauri::Manager;
|
||||||
use tauri::{AppHandle, Runtime};
|
use tauri::{AppHandle, Runtime};
|
||||||
|
#[cfg(windows)]
|
||||||
|
use windows_registry::CURRENT_USER;
|
||||||
|
|
||||||
/// Access to the deep-link APIs.
|
/// Access to the deep-link APIs.
|
||||||
pub struct DeepLink<R: Runtime> {
|
pub struct DeepLink<R: Runtime> {
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub(crate) app: AppHandle<R>,
|
pub(crate) app: AppHandle<R>,
|
||||||
|
#[allow(dead_code)]
|
||||||
pub(crate) current: Mutex<Option<Vec<url::Url>>>,
|
pub(crate) current: Mutex<Option<Vec<url::Url>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R: Runtime> DeepLink<R> {
|
impl<R: Runtime> DeepLink<R> {
|
||||||
/// Get the current URLs that triggered the deep link.
|
/// 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`).
|
||||||
pub fn get_current(&self) -> crate::Result<Option<Vec<url::Url>>> {
|
pub fn get_current(&self) -> crate::Result<Option<Vec<url::Url>>> {
|
||||||
Ok(self.current.lock().unwrap().clone())
|
#[cfg(not(any(windows, target_os = "linux")))]
|
||||||
|
return Ok(self.current.lock().unwrap().clone());
|
||||||
|
#[cfg(any(windows, target_os = "linux"))]
|
||||||
|
Err(crate::Error::UnsupportedPlatform)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Register the app as the default handler for the specified protocol.
|
||||||
|
///
|
||||||
|
/// - `protocol`: The name of the protocol without `://`. For example, if you want your app to handle `tauri://` links, call this method with `tauri` as the protocol.
|
||||||
|
///
|
||||||
|
/// ## Platform-specific:
|
||||||
|
///
|
||||||
|
/// - **macOS / Android / iOS**: Unsupported, will return [`Error::UnsupportedPlatform`](`crate::Error::UnsupportedPlatform`).
|
||||||
|
pub fn register<S: AsRef<str>>(&self, _protocol: S) -> crate::Result<()> {
|
||||||
|
#[cfg(windows)]
|
||||||
|
{
|
||||||
|
let key_base = format!("Software\\Classes\\{}", _protocol.as_ref());
|
||||||
|
|
||||||
|
let exe = dunce::simplified(&tauri::utils::platform::current_exe()?)
|
||||||
|
.display()
|
||||||
|
.to_string();
|
||||||
|
|
||||||
|
let key_reg = CURRENT_USER.create(&key_base)?;
|
||||||
|
key_reg.set_string(
|
||||||
|
"",
|
||||||
|
&format!("URL:{} protocol", self.app.config().identifier),
|
||||||
|
)?;
|
||||||
|
key_reg.set_string("URL Protocol", "")?;
|
||||||
|
|
||||||
|
let icon_reg = CURRENT_USER.create(format!("{key_base}\\DefaultIcon"))?;
|
||||||
|
icon_reg.set_string("", &format!("{},0", &exe))?;
|
||||||
|
|
||||||
|
let cmd_reg = CURRENT_USER.create(format!("{key_base}\\shell\\open\\command"))?;
|
||||||
|
|
||||||
|
cmd_reg.set_string("", &format!("{} \"%1\"", &exe))?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
{
|
||||||
|
let bin = tauri::utils::platform::current_exe()?;
|
||||||
|
let file_name = format!(
|
||||||
|
"{}-handler.desktop",
|
||||||
|
bin.file_name().unwrap().to_string_lossy()
|
||||||
|
);
|
||||||
|
let appimage = self.app.env().appimage;
|
||||||
|
let exec = appimage
|
||||||
|
.clone()
|
||||||
|
.unwrap_or_else(|| bin.into_os_string())
|
||||||
|
.to_string_lossy()
|
||||||
|
.to_string();
|
||||||
|
|
||||||
|
let target = self.app.path().data_dir()?.join("applications");
|
||||||
|
|
||||||
|
create_dir_all(&target)?;
|
||||||
|
|
||||||
|
let target_file = target.join(&file_name);
|
||||||
|
|
||||||
|
let mime_type = format!("x-scheme-handler/{};", _protocol.as_ref());
|
||||||
|
|
||||||
|
if let Ok(mut desktop_file) = ini::Ini::load_from_file(&target_file) {
|
||||||
|
if let Some(section) = desktop_file.section_mut(Some("Desktop Entry")) {
|
||||||
|
if let Some(mimes) = section.remove("MimeType") {
|
||||||
|
section.append("MimeType", format!("{mimes};{mime_type};"))
|
||||||
|
} else {
|
||||||
|
section.append("MimeType", format!("{mime_type};"))
|
||||||
|
}
|
||||||
|
desktop_file.write_to_file(&target_file)?;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let mut file = File::create(target_file)?;
|
||||||
|
file.write_all(
|
||||||
|
format!(
|
||||||
|
include_str!("template.desktop"),
|
||||||
|
name = self
|
||||||
|
.app
|
||||||
|
.config()
|
||||||
|
.product_name
|
||||||
|
.clone()
|
||||||
|
.unwrap_or_else(|| file_name.clone()),
|
||||||
|
exec = exec,
|
||||||
|
mime_type = mime_type
|
||||||
|
)
|
||||||
|
.as_bytes(),
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Command::new("update-desktop-database")
|
||||||
|
.arg(target)
|
||||||
|
.status()?;
|
||||||
|
|
||||||
|
Command::new("xdg-mime")
|
||||||
|
.args(["default", &file_name, _protocol.as_ref()])
|
||||||
|
.status()?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(any(windows, target_os = "linux")))]
|
||||||
|
Err(crate::Error::UnsupportedPlatform)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Unregister the app as the default handler for the specified protocol.
|
||||||
|
///
|
||||||
|
/// - `protocol`: The name of the protocol without `://`.
|
||||||
|
///
|
||||||
|
/// ## Platform-specific:
|
||||||
|
///
|
||||||
|
/// - **Linux**: Can only unregister the scheme if it was initially registered with [`register`](`Self::register`). May not work on older distros.
|
||||||
|
/// - **macOS / Android / iOS**: Unsupported, will return [`Error::UnsupportedPlatform`](`crate::Error::UnsupportedPlatform`).
|
||||||
|
pub fn unregister<S: AsRef<str>>(&self, _protocol: S) -> crate::Result<()> {
|
||||||
|
#[cfg(windows)]
|
||||||
|
{
|
||||||
|
CURRENT_USER.remove_tree(format!("Software\\Classes\\{}", _protocol.as_ref()))?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
{
|
||||||
|
let mimeapps_path = self.app.path().config_dir()?.join("mimeapps.list");
|
||||||
|
let mut mimeapps = ini::Ini::load_from_file(&mimeapps_path)?;
|
||||||
|
|
||||||
|
let file_name = format!(
|
||||||
|
"{}-handler.desktop",
|
||||||
|
tauri::utils::platform::current_exe()?
|
||||||
|
.file_name()
|
||||||
|
.unwrap()
|
||||||
|
.to_string_lossy()
|
||||||
|
);
|
||||||
|
|
||||||
|
if let Some(section) = mimeapps.section_mut(Some("Default Applications")) {
|
||||||
|
let scheme = format!("x-scheme-handler/{}", _protocol.as_ref());
|
||||||
|
|
||||||
|
if section.get(&scheme).unwrap_or_default() == file_name {
|
||||||
|
section.remove(scheme);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mimeapps.write_to_file(mimeapps_path)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(any(windows, target_os = "linux")))]
|
||||||
|
Err(crate::Error::UnsupportedPlatform)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check whether the app is the default handler for the specified protocol.
|
||||||
|
///
|
||||||
|
/// - `protocol`: The name of the protocol without `://`.
|
||||||
|
///
|
||||||
|
/// ## Platform-specific:
|
||||||
|
///
|
||||||
|
/// - **macOS / Android / iOS**: Unsupported, will return [`Error::UnsupportedPlatform`](`crate::Error::UnsupportedPlatform`).
|
||||||
|
pub fn is_registered<S: AsRef<str>>(&self, _protocol: S) -> crate::Result<bool> {
|
||||||
|
#[cfg(windows)]
|
||||||
|
{
|
||||||
|
let cmd_reg = CURRENT_USER.open(format!(
|
||||||
|
"Software\\Classes\\{}\\shell\\open\\command",
|
||||||
|
_protocol.as_ref()
|
||||||
|
))?;
|
||||||
|
|
||||||
|
let registered_cmd: String = cmd_reg.get_string("")?;
|
||||||
|
|
||||||
|
let exe = dunce::simplified(&tauri::utils::platform::current_exe()?)
|
||||||
|
.display()
|
||||||
|
.to_string();
|
||||||
|
|
||||||
|
Ok(registered_cmd == format!("{} \"%1\"", &exe))
|
||||||
|
}
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
{
|
||||||
|
let file_name = format!(
|
||||||
|
"{}-handler.desktop",
|
||||||
|
tauri::utils::platform::current_exe()?
|
||||||
|
.file_name()
|
||||||
|
.unwrap()
|
||||||
|
.to_string_lossy()
|
||||||
|
);
|
||||||
|
|
||||||
|
let output = Command::new("xdg-mime")
|
||||||
|
.args([
|
||||||
|
"query",
|
||||||
|
"default",
|
||||||
|
&format!("x-scheme-handler/{}", _protocol.as_ref()),
|
||||||
|
])
|
||||||
|
.output()?;
|
||||||
|
|
||||||
|
Ok(String::from_utf8_lossy(&output.stdout).contains(&file_name))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(any(windows, target_os = "linux")))]
|
||||||
|
Err(crate::Error::UnsupportedPlatform)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -128,7 +375,12 @@ impl<R: Runtime, T: Manager<R>> crate::DeepLinkExt<R> for T {
|
|||||||
/// Initializes the plugin.
|
/// Initializes the plugin.
|
||||||
pub fn init<R: Runtime>() -> TauriPlugin<R, Option<config::Config>> {
|
pub fn init<R: Runtime>() -> TauriPlugin<R, Option<config::Config>> {
|
||||||
Builder::new("deep-link")
|
Builder::new("deep-link")
|
||||||
.invoke_handler(tauri::generate_handler![commands::get_current])
|
.invoke_handler(tauri::generate_handler![
|
||||||
|
commands::get_current,
|
||||||
|
commands::register,
|
||||||
|
commands::unregister,
|
||||||
|
commands::is_registered
|
||||||
|
])
|
||||||
.setup(|app, api| {
|
.setup(|app, api| {
|
||||||
app.manage(init_deep_link(app, api)?);
|
app.manage(init_deep_link(app, api)?);
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
[Desktop Entry]
|
||||||
|
Type=Application
|
||||||
|
Name={name}
|
||||||
|
Exec={exec} %u
|
||||||
|
Terminal=false
|
||||||
|
MimeType={mime_type}
|
||||||
|
NoDisplay=true
|
||||||
Reference in New Issue
Block a user