Merge branch 'v2' into feature/fallback_targets

This commit is contained in:
Krzysztof Andrelczyk
2025-07-08 23:55:33 +02:00
202 changed files with 3272 additions and 2454 deletions
+8
View File
@@ -1,5 +1,13 @@
# Changelog
## \[2.5.0]
- [`f209b2f2`](https://github.com/tauri-apps/plugins-workspace/commit/f209b2f23cb29133c97ad5961fb46ef794dbe063) ([#2804](https://github.com/tauri-apps/plugins-workspace/pull/2804) by [@renovate](https://github.com/tauri-apps/plugins-workspace/../../renovate)) Updated tauri to 2.6
## \[2.4.0]
- [`764e8f77`](https://github.com/tauri-apps/plugins-workspace/commit/764e8f7719247da515243d9c9cafa6d087d21769) ([#2707](https://github.com/tauri-apps/plugins-workspace/pull/2707)) Added a new builder method app_name() to allow customizing the application name in the autostart entry.
## \[2.3.0]
- [`8ecb418a`](https://github.com/tauri-apps/plugins-workspace/commit/8ecb418a1a35d7f234dc5d833746ac2d8e062aec) ([#2569](https://github.com/tauri-apps/plugins-workspace/pull/2569)) Add a `Builder` for more flexible settings
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "tauri-plugin-autostart"
version = "2.3.0"
version = "2.5.0"
description = "Automatically launch your application at startup."
authors = { workspace = true }
license = { workspace = true }
+4 -1
View File
@@ -59,7 +59,10 @@ First you need to register the core plugin with Tauri:
```rust
fn main() {
tauri::Builder::default()
.plugin(tauri_plugin_autostart::Builder::new().args((["--flag1", "--flag2"])).build()))
.plugin(tauri_plugin_autostart::Builder::new()
.args(["--flag1", "--flag2"])
.app_name("My Custom Name")
.build())
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@tauri-apps/plugin-autostart",
"version": "2.3.0",
"version": "2.5.0",
"license": "MIT OR Apache-2.0",
"authors": [
"Tauri Programme within The Commons Conservancy"
@@ -24,6 +24,6 @@
"LICENSE"
],
"dependencies": {
"@tauri-apps/api": "^2.0.0"
"@tauri-apps/api": "^2.6.0"
}
}
@@ -9,8 +9,6 @@ starting the application on boot.
It allows all to check, enable and
disable the automatic start on boot.
#### This default permission set includes the following:
- `allow-enable`
+22 -1
View File
@@ -103,6 +103,7 @@ pub struct Builder {
#[cfg(target_os = "macos")]
macos_launcher: MacosLauncher,
args: Vec<String>,
app_name: Option<String>,
}
impl Builder {
@@ -154,12 +155,32 @@ impl Builder {
self
}
/// Sets the app name to be used for the auto start entry.
///
/// ## Examples
///
/// ```no_run
/// Builder::new()
/// .app_name("My Custom Name"))
/// .build();
/// ```
pub fn app_name<S: Into<String>>(mut self, app_name: S) -> Self {
self.app_name = Some(app_name.into());
self
}
pub fn build<R: Runtime>(self) -> TauriPlugin<R> {
PluginBuilder::new("autostart")
.invoke_handler(tauri::generate_handler![enable, disable, is_enabled])
.setup(move |app, _api| {
let mut builder = AutoLaunchBuilder::new();
builder.set_app_name(&app.package_info().name);
let app_name = self
.app_name
.as_ref()
.unwrap_or_else(|| &app.package_info().name);
builder.set_app_name(app_name);
builder.set_args(&self.args);
let current_exe = current_exe()?;
+8
View File
@@ -1,5 +1,13 @@
# Changelog
## \[2.3.0]
- [`f209b2f2`](https://github.com/tauri-apps/plugins-workspace/commit/f209b2f23cb29133c97ad5961fb46ef794dbe063) ([#2804](https://github.com/tauri-apps/plugins-workspace/pull/2804) by [@renovate](https://github.com/tauri-apps/plugins-workspace/../../renovate)) Updated tauri to 2.6
## \[2.2.1]
- [`f634e524`](https://github.com/tauri-apps/plugins-workspace/commit/f634e5248ebe428f8305a59f74c13fc15147fb8e) This is an "empty" release to update the plugins' source files on crates.io and docs.rs. This should fix docs.rs build failures for projects using tauri plugins as dependencies.
## \[2.2.0]
- [`3a79266b`](https://github.com/tauri-apps/plugins-workspace/commit/3a79266b8cf96a55b1ae6339d725567d45a44b1d) ([#2173](https://github.com/tauri-apps/plugins-workspace/pull/2173) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) Bumped all plugins to `v2.2.0`. From now, the versions for the Rust and JavaScript packages of each plugin will be in sync with each other.
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "tauri-plugin-barcode-scanner"
version = "2.2.0"
version = "2.3.0"
description = "Scan QR codes, EAN-13 and other kinds of barcodes on Android and iOS"
edition = { workspace = true }
authors = { workspace = true }
+1 -1
View File
@@ -1 +1 @@
if("__TAURI__"in window){var __TAURI_PLUGIN_BARCODE_SCANNER__=function(n){"use strict";async function e(n,e={},r){return window.__TAURI_INTERNALS__.invoke(n,e,r)}var r;return"function"==typeof SuppressedError&&SuppressedError,n.Format=void 0,(r=n.Format||(n.Format={})).QRCode="QR_CODE",r.UPC_A="UPC_A",r.UPC_E="UPC_E",r.EAN8="EAN_8",r.EAN13="EAN_13",r.Code39="CODE_39",r.Code93="CODE_93",r.Code128="CODE_128",r.Codabar="CODABAR",r.ITF="ITF",r.Aztec="AZTEC",r.DataMatrix="DATA_MATRIX",r.PDF417="PDF_417",n.cancel=async function(){await e("plugin:barcode-scanner|cancel")},n.checkPermissions=async function(){return await async function(n){return e(`plugin:${n}|check_permissions`)}("barcode-scanner").then((n=>n.camera))},n.openAppSettings=async function(){await e("plugin:barcode-scanner|open_app_settings")},n.requestPermissions=async function(){return await async function(n){return e(`plugin:${n}|request_permissions`)}("barcode-scanner").then((n=>n.camera))},n.scan=async function(n){return await e("plugin:barcode-scanner|scan",{...n})},n}({});Object.defineProperty(window.__TAURI__,"barcodeScanner",{value:__TAURI_PLUGIN_BARCODE_SCANNER__})}
if("__TAURI__"in window){var __TAURI_PLUGIN_BARCODE_SCANNER__=function(n){"use strict";async function a(n,a={},e){return window.__TAURI_INTERNALS__.invoke(n,a,e)}var e;return"function"==typeof SuppressedError&&SuppressedError,n.Format=void 0,(e=n.Format||(n.Format={})).QRCode="QR_CODE",e.UPC_A="UPC_A",e.UPC_E="UPC_E",e.EAN8="EAN_8",e.EAN13="EAN_13",e.Code39="CODE_39",e.Code93="CODE_93",e.Code128="CODE_128",e.Codabar="CODABAR",e.ITF="ITF",e.Aztec="AZTEC",e.DataMatrix="DATA_MATRIX",e.PDF417="PDF_417",e.GS1DataBar="GS1_DATA_BAR",e.GS1DataBarLimited="GS1_DATA_BAR_LIMITED",e.GS1DataBarExpanded="GS1_DATA_BAR_EXPANDED",n.cancel=async function(){await a("plugin:barcode-scanner|cancel")},n.checkPermissions=async function(){return await async function(n){return a(`plugin:${n}|check_permissions`)}("barcode-scanner").then((n=>n.camera))},n.openAppSettings=async function(){await a("plugin:barcode-scanner|open_app_settings")},n.requestPermissions=async function(){return await async function(n){return a(`plugin:${n}|request_permissions`)}("barcode-scanner").then((n=>n.camera))},n.scan=async function(n){return await a("plugin:barcode-scanner|scan",{...n})},n}({});Object.defineProperty(window.__TAURI__,"barcodeScanner",{value:__TAURI_PLUGIN_BARCODE_SCANNER__})}
+19 -1
View File
@@ -12,6 +12,9 @@ export type { PermissionState } from '@tauri-apps/api/core'
export enum Format {
QRCode = 'QR_CODE',
/**
* Not supported on iOS.
*/
UPC_A = 'UPC_A',
UPC_E = 'UPC_E',
EAN8 = 'EAN_8',
@@ -19,11 +22,26 @@ export enum Format {
Code39 = 'CODE_39',
Code93 = 'CODE_93',
Code128 = 'CODE_128',
/**
* Not supported on iOS.
*/
Codabar = 'CODABAR',
ITF = 'ITF',
Aztec = 'AZTEC',
DataMatrix = 'DATA_MATRIX',
PDF417 = 'PDF_417'
PDF417 = 'PDF_417',
/**
* Not supported on Android. Requires iOS 15.4+
*/
GS1DataBar = 'GS1_DATA_BAR',
/**
* Not supported on Android. Requires iOS 15.4+
*/
GS1DataBarLimited = 'GS1_DATA_BAR_LIMITED',
/**
* Not supported on Android. Requires iOS 15.4+
*/
GS1DataBarExpanded = 'GS1_DATA_BAR_EXPANDED'
}
export interface ScanOptions {
@@ -27,8 +27,11 @@ enum SupportedFormat: String, CaseIterable, Decodable {
case DATA_MATRIX
case PDF_417
case QR_CODE
case GS1_DATA_BAR
case GS1_DATA_BAR_LIMITED
case GS1_DATA_BAR_EXPANDED
var value: AVMetadataObject.ObjectType {
var value: AVMetadataObject.ObjectType? {
switch self {
case .UPC_E: return AVMetadataObject.ObjectType.upce
case .EAN_8: return AVMetadataObject.ObjectType.ean8
@@ -41,6 +44,24 @@ enum SupportedFormat: String, CaseIterable, Decodable {
case .DATA_MATRIX: return AVMetadataObject.ObjectType.dataMatrix
case .PDF_417: return AVMetadataObject.ObjectType.pdf417
case .QR_CODE: return AVMetadataObject.ObjectType.qr
case .GS1_DATA_BAR:
if #available(iOS 15.4, *) {
return AVMetadataObject.ObjectType.gs1DataBar
} else {
return nil
}
case .GS1_DATA_BAR_LIMITED:
if #available(iOS 15.4, *) {
return AVMetadataObject.ObjectType.gs1DataBarLimited
} else {
return nil
}
case .GS1_DATA_BAR_EXPANDED:
if #available(iOS 15.4, *) {
return AVMetadataObject.ObjectType.gs1DataBarExpanded
} else {
return nil
}
}
}
}
@@ -242,13 +263,20 @@ class BarcodeScannerPlugin: Plugin, AVCaptureMetadataOutputObjectsDelegate {
scanFormats = [AVMetadataObject.ObjectType]()
(args.formats ?? []).forEach { format in
scanFormats.append(format.value)
if let formatValue = format.value {
scanFormats.append(formatValue)
} else {
invoke.reject("Unsupported barcode format on this iOS version: \(format)")
return
}
}
if scanFormats.count == 0 {
for supportedFormat in SupportedFormat.allCases {
scanFormats.append(supportedFormat.value)
}
if scanFormats.isEmpty {
for supportedFormat in SupportedFormat.allCases {
if let formatValue = supportedFormat.value {
scanFormats.append(formatValue)
}
}
}
self.metaOutput!.metadataObjectTypes = self.scanFormats
@@ -52,6 +52,15 @@ func discoverCaptureDevices() -> [AVCaptureDevice] {
}
func formatStringFromMetadata(_ type: AVMetadataObject.ObjectType) -> String {
if #available(iOS 15.4, *) {
if type == .gs1DataBar {
return "GS1_DATA_BAR"
} else if type == .gs1DataBarLimited {
return "GS1_DATA_BAR_LIMITED"
} else if type == .gs1DataBarExpanded {
return "GS1_DATA_BAR_EXPANDED"
}
}
switch type {
case AVMetadataObject.ObjectType.upce:
return "UPC_E"
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@tauri-apps/plugin-barcode-scanner",
"version": "2.2.0",
"version": "2.3.0",
"description": "Scan QR codes, EAN-13 and other kinds of barcodes on Android and iOS",
"license": "MIT OR Apache-2.0",
"authors": [
@@ -25,6 +25,6 @@
"LICENSE"
],
"dependencies": {
"@tauri-apps/api": "^2.0.0"
"@tauri-apps/api": "^2.6.0"
}
}
@@ -7,8 +7,6 @@ barcode scanning features are by default exposed.
It allows all barcode related features.
#### This default permission set includes the following:
- `allow-cancel`
+14
View File
@@ -1,5 +1,19 @@
# Changelog
## \[2.3.0]
- [`f209b2f2`](https://github.com/tauri-apps/plugins-workspace/commit/f209b2f23cb29133c97ad5961fb46ef794dbe063) ([#2804](https://github.com/tauri-apps/plugins-workspace/pull/2804) by [@renovate](https://github.com/tauri-apps/plugins-workspace/../../renovate)) Updated tauri to 2.6
## \[2.2.2]
- [`f634e524`](https://github.com/tauri-apps/plugins-workspace/commit/f634e5248ebe428f8305a59f74c13fc15147fb8e) This is an "empty" release to update the plugins' source files on crates.io and docs.rs. This should fix docs.rs build failures for projects using tauri plugins as dependencies.
## \[2.2.1]
### bug
- [`10f9e66e`](https://github.com/tauri-apps/plugins-workspace/commit/10f9e66e32141dd35f4bf884fbf9102691187e92) ([#2633](https://github.com/tauri-apps/plugins-workspace/pull/2633) by [@pjf-dev](https://github.com/tauri-apps/plugins-workspace/../../pjf-dev)) Fix biometric plugin ignoring fallback logic when biometry status is unavailable or not enrolled on iOS.
## \[2.2.0]
- [`3a79266b`](https://github.com/tauri-apps/plugins-workspace/commit/3a79266b8cf96a55b1ae6339d725567d45a44b1d) ([#2173](https://github.com/tauri-apps/plugins-workspace/pull/2173) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) Bumped all plugins to `v2.2.0`. From now, the versions for the Rust and JavaScript packages of each plugin will be in sync with each other.
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "tauri-plugin-biometric"
version = "2.2.0"
version = "2.3.0"
description = "Prompt the user for biometric authentication on Android and iOS."
edition = { workspace = true }
authors = { workspace = true }
@@ -98,7 +98,12 @@ class BiometricPlugin: Plugin {
}
@objc func authenticate(_ invoke: Invoke) throws {
guard self.status.available else {
let args = try invoke.parseArgs(AuthOptions.self)
let allowDeviceCredential = args.allowDeviceCredential ?? false
guard self.status.available || allowDeviceCredential else {
// Biometry unavailable, fallback disabled
invoke.reject(
self.status.errorReason ?? "",
code: self.status.errorCode ?? ""
@@ -106,15 +111,11 @@ class BiometricPlugin: Plugin {
return
}
let args = try invoke.parseArgs(AuthOptions.self)
let context = LAContext()
context.localizedFallbackTitle = args.fallbackTitle
context.localizedCancelTitle = args.cancelTitle
context.touchIDAuthenticationAllowableReuseDuration = 0
let allowDeviceCredential = args.allowDeviceCredential ?? false
// force system default fallback title if an empty string is provided (the OS hides the fallback button in this case)
if allowDeviceCredential,
let fallbackTitle = context.localizedFallbackTitle,
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@tauri-apps/plugin-biometric",
"version": "2.2.0",
"version": "2.3.0",
"license": "MIT OR Apache-2.0",
"authors": [
"Tauri Programme within The Commons Conservancy"
@@ -25,6 +25,6 @@
"LICENSE"
],
"dependencies": {
"@tauri-apps/api": "^2.0.0"
"@tauri-apps/api": "^2.6.0"
}
}
@@ -7,8 +7,6 @@ biometric features are by default exposed.
It allows acccess to all biometric commands.
#### This default permission set includes the following:
- `allow-authenticate`
+15
View File
@@ -1,5 +1,20 @@
# Changelog
## \[2.4.0]
- [`f209b2f2`](https://github.com/tauri-apps/plugins-workspace/commit/f209b2f23cb29133c97ad5961fb46ef794dbe063) ([#2804](https://github.com/tauri-apps/plugins-workspace/pull/2804) by [@renovate](https://github.com/tauri-apps/plugins-workspace/../../renovate)) Updated tauri to 2.6
## \[2.3.0]
- [`f6e11282`](https://github.com/tauri-apps/plugins-workspace/commit/f6e11282a7f4036dd6d1dbb8f100e777e9e42f11) ([#2787](https://github.com/tauri-apps/plugins-workspace/pull/2787) by [@mikew](https://github.com/tauri-apps/plugins-workspace/../../mikew)) Added `Cli.matches_from(args)`. This can be combined with the `args` passed to the callback of `tauri_plugin_single_instance::init` to parse the command line arguments passed to subsequent instances of the application.
- [`37c2fb41`](https://github.com/tauri-apps/plugins-workspace/commit/37c2fb41201160e85c8dc3ad40f462cd4e17a304) ([#2772](https://github.com/tauri-apps/plugins-workspace/pull/2772) by [@floriskn](https://github.com/tauri-apps/plugins-workspace/../../floriskn)) Added a new `global` boolean flag to the `CliArg` struct to support global CLI arguments. This flag allows arguments like `--verbose` to be marked as global and automatically propagated to all subcommands, enabling consistent availability throughout the CLI.
The new field is optional and defaults to false.
## \[2.2.1]
- [`f634e524`](https://github.com/tauri-apps/plugins-workspace/commit/f634e5248ebe428f8305a59f74c13fc15147fb8e) This is an "empty" release to update the plugins' source files on crates.io and docs.rs. This should fix docs.rs build failures for projects using tauri plugins as dependencies.
## \[2.2.0]
- [`3a79266b`](https://github.com/tauri-apps/plugins-workspace/commit/3a79266b8cf96a55b1ae6339d725567d45a44b1d) ([#2173](https://github.com/tauri-apps/plugins-workspace/pull/2173) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) Bumped all plugins to `v2.2.0`. From now, the versions for the Rust and JavaScript packages of each plugin will be in sync with each other.
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "tauri-plugin-cli"
version = "2.2.0"
version = "2.4.0"
description = "Parse arguments from your Tauri application's command line interface."
edition = { workspace = true }
authors = { workspace = true }
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@tauri-apps/plugin-cli",
"version": "2.2.0",
"version": "2.4.0",
"license": "MIT OR Apache-2.0",
"authors": [
"Tauri Programme within The Commons Conservancy"
@@ -24,6 +24,6 @@
"LICENSE"
],
"dependencies": {
"@tauri-apps/api": "^2.0.0"
"@tauri-apps/api": "^2.6.0"
}
}
+6
View File
@@ -114,6 +114,12 @@ pub struct Arg {
/// It does not define position in the argument list as a whole. When utilized with multiple=true,
/// only the last positional argument may be defined as multiple (i.e. the one with the highest index).
pub index: Option<usize>,
/// Specifies whether the argument should be global.
///
/// Global arguments are propagated to all subcommands automatically,
/// making them available throughout the CLI regardless of where they are defined.
#[serde(default)]
pub global: bool,
}
/// describes a CLI configuration
+5 -1
View File
@@ -29,7 +29,11 @@ pub struct Cli<R: Runtime>(PluginApi<R, Config>);
impl<R: Runtime> Cli<R> {
pub fn matches(&self) -> Result<parser::Matches> {
parser::get_matches(self.0.config(), self.0.app().package_info())
parser::get_matches(self.0.config(), self.0.app().package_info(), None)
}
pub fn matches_from(&self, args: Vec<String>) -> Result<parser::Matches> {
parser::get_matches(self.0.config(), self.0.app().package_info(), Some(args))
}
}
+18 -5
View File
@@ -19,7 +19,7 @@ use std::collections::HashMap;
mod macros;
/// The resolution of a argument match.
#[derive(Default, Debug, Serialize)]
#[derive(Default, Debug, Serialize, Clone)]
#[non_exhaustive]
pub struct ArgData {
/// - [`Value::Bool`] if it's a flag,
@@ -33,7 +33,7 @@ pub struct ArgData {
}
/// The matched subcommand.
#[derive(Default, Debug, Serialize)]
#[derive(Default, Debug, Serialize, Clone)]
#[non_exhaustive]
pub struct SubcommandMatches {
/// The subcommand name.
@@ -43,7 +43,7 @@ pub struct SubcommandMatches {
}
/// The argument matches of a command.
#[derive(Default, Debug, Serialize)]
#[derive(Default, Debug, Serialize, Clone)]
#[non_exhaustive]
pub struct Matches {
/// Data structure mapping each found arg with its resolution.
@@ -79,7 +79,11 @@ impl Matches {
/// Ok(())
/// });
/// ```
pub fn get_matches(cli: &Config, package_info: &PackageInfo) -> crate::Result<Matches> {
pub fn get_matches(
cli: &Config,
package_info: &PackageInfo,
args: Option<Vec<String>>,
) -> crate::Result<Matches> {
let about = cli
.description()
.unwrap_or(&package_info.description.to_string())
@@ -92,7 +96,14 @@ pub fn get_matches(cli: &Config, package_info: &PackageInfo) -> crate::Result<Ma
Some(&about),
cli,
);
match app.try_get_matches() {
let matches = if let Some(args) = args {
app.try_get_matches_from(args)
} else {
app.try_get_matches()
};
match matches {
Ok(matches) => Ok(get_matches_internal(cli, &matches)),
Err(e) => match e.kind() {
ErrorKind::DisplayHelp => {
@@ -278,5 +289,7 @@ fn get_arg(arg_name: String, arg: &Arg) -> ClapArg {
clap_arg = bind_value_arg!(arg, clap_arg, require_equals);
clap_arg = bind_value_arg!(arg, clap_arg, index);
clap_arg = clap_arg.global(arg.global);
clap_arg
}
+8
View File
@@ -1,5 +1,13 @@
# Changelog
## \[2.3.0]
- [`f209b2f2`](https://github.com/tauri-apps/plugins-workspace/commit/f209b2f23cb29133c97ad5961fb46ef794dbe063) ([#2804](https://github.com/tauri-apps/plugins-workspace/pull/2804) by [@renovate](https://github.com/tauri-apps/plugins-workspace/../../renovate)) Updated tauri to 2.6
## \[2.2.3]
- [`f634e524`](https://github.com/tauri-apps/plugins-workspace/commit/f634e5248ebe428f8305a59f74c13fc15147fb8e) This is an "empty" release to update the plugins' source files on crates.io and docs.rs. This should fix docs.rs build failures for projects using tauri plugins as dependencies.
## \[2.2.2]
### bug
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "tauri-plugin-clipboard-manager"
version = "2.2.2"
version = "2.3.0"
description = "Read and write to the system clipboard."
edition = { workspace = true }
authors = { workspace = true }
+1 -1
View File
@@ -1 +1 @@
if("__TAURI__"in window){var __TAURI_PLUGIN_CLIPBOARD_MANAGER__=function(e){"use strict";var n;async function t(e,n={},t){return window.__TAURI_INTERNALS__.invoke(e,n,t)}"function"==typeof SuppressedError&&SuppressedError;class r{get rid(){return function(e,n,t,r){if("function"==typeof n||!n.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===t?r:"a"===t?r.call(e):r?r.value:n.get(e)}(this,n,"f")}constructor(e){n.set(this,void 0),function(e,n,t){if("function"==typeof n||!n.has(e))throw new TypeError("Cannot write private member to an object whose class did not declare it");n.set(e,t)}(this,n,e)}async close(){return t("plugin:resources|close",{rid:this.rid})}}n=new WeakMap;class a extends r{constructor(e){super(e)}static async new(e,n,r){return t("plugin:image|new",{rgba:i(e),width:n,height:r}).then((e=>new a(e)))}static async fromBytes(e){return t("plugin:image|from_bytes",{bytes:i(e)}).then((e=>new a(e)))}static async fromPath(e){return t("plugin:image|from_path",{path:e}).then((e=>new a(e)))}async rgba(){return t("plugin:image|rgba",{rid:this.rid}).then((e=>new Uint8Array(e)))}async size(){return t("plugin:image|size",{rid:this.rid})}}function i(e){return null==e?null:"string"==typeof e?e:e instanceof a?e.rid:e}return e.clear=async function(){await t("plugin:clipboard-manager|clear")},e.readImage=async function(){return await t("plugin:clipboard-manager|read_image").then((e=>new a(e)))},e.readText=async function(){return await t("plugin:clipboard-manager|read_text")},e.writeHtml=async function(e,n){await t("plugin:clipboard-manager|write_html",{html:e,altText:n})},e.writeImage=async function(e){await t("plugin:clipboard-manager|write_image",{image:i(e)})},e.writeText=async function(e,n){await t("plugin:clipboard-manager|write_text",{label:n?.label,text:e})},e}({});Object.defineProperty(window.__TAURI__,"clipboardManager",{value:__TAURI_PLUGIN_CLIPBOARD_MANAGER__})}
if("__TAURI__"in window){var __TAURI_PLUGIN_CLIPBOARD_MANAGER__=function(e){"use strict";var n;async function t(e,n={},t){return window.__TAURI_INTERNALS__.invoke(e,n,t)}"function"==typeof SuppressedError&&SuppressedError;class r{get rid(){return function(e,n,t,r){if("function"==typeof n?e!==n||!r:!n.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===t?r:"a"===t?r.call(e):r?r.value:n.get(e)}(this,n,"f")}constructor(e){n.set(this,void 0),function(e,n,t){if("function"==typeof n||!n.has(e))throw new TypeError("Cannot write private member to an object whose class did not declare it");n.set(e,t)}(this,n,e)}async close(){return t("plugin:resources|close",{rid:this.rid})}}n=new WeakMap;class a extends r{constructor(e){super(e)}static async new(e,n,r){return t("plugin:image|new",{rgba:i(e),width:n,height:r}).then((e=>new a(e)))}static async fromBytes(e){return t("plugin:image|from_bytes",{bytes:i(e)}).then((e=>new a(e)))}static async fromPath(e){return t("plugin:image|from_path",{path:e}).then((e=>new a(e)))}async rgba(){return t("plugin:image|rgba",{rid:this.rid}).then((e=>new Uint8Array(e)))}async size(){return t("plugin:image|size",{rid:this.rid})}}function i(e){return null==e?null:"string"==typeof e?e:e instanceof a?e.rid:e}return e.clear=async function(){await t("plugin:clipboard-manager|clear")},e.readImage=async function(){return await t("plugin:clipboard-manager|read_image").then((e=>new a(e)))},e.readText=async function(){return await t("plugin:clipboard-manager|read_text")},e.writeHtml=async function(e,n){await t("plugin:clipboard-manager|write_html",{html:e,altText:n})},e.writeImage=async function(e){await t("plugin:clipboard-manager|write_image",{image:i(e)})},e.writeText=async function(e,n){await t("plugin:clipboard-manager|write_text",{label:n?.label,text:e})},e}({});Object.defineProperty(window.__TAURI__,"clipboardManager",{value:__TAURI_PLUGIN_CLIPBOARD_MANAGER__})}
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@tauri-apps/plugin-clipboard-manager",
"version": "2.2.2",
"version": "2.3.0",
"license": "MIT OR Apache-2.0",
"authors": [
"Tauri Programme within The Commons Conservancy"
@@ -24,6 +24,6 @@
"LICENSE"
],
"dependencies": {
"@tauri-apps/api": "^2.0.0"
"@tauri-apps/api": "^2.6.0"
}
}
@@ -6,10 +6,6 @@ application specific if read and/or write access is needed.
Clipboard interaction needs to be explicitly enabled.
#### This default permission set includes the following:
## Permission Table
<table>
+9
View File
@@ -1,5 +1,14 @@
# Changelog
## \[2.4.0]
- [`f209b2f2`](https://github.com/tauri-apps/plugins-workspace/commit/f209b2f23cb29133c97ad5961fb46ef794dbe063) ([#2804](https://github.com/tauri-apps/plugins-workspace/pull/2804) by [@renovate](https://github.com/tauri-apps/plugins-workspace/../../renovate)) Updated tauri to 2.6
## \[2.3.0]
- [`4d10acee`](https://github.com/tauri-apps/plugins-workspace/commit/4d10acee61bad8045705508121424ed5f2d381f6) ([#993](https://github.com/tauri-apps/plugins-workspace/pull/993) by [@m00nwtchr](https://github.com/tauri-apps/plugins-workspace/../../m00nwtchr)) Exposed Android's `path`, `pathPattern` and `pathSuffix` configurations.
- [`4d10acee`](https://github.com/tauri-apps/plugins-workspace/commit/4d10acee61bad8045705508121424ed5f2d381f6) ([#993](https://github.com/tauri-apps/plugins-workspace/pull/993) by [@m00nwtchr](https://github.com/tauri-apps/plugins-workspace/../../m00nwtchr)) Added a `scheme` configuration to set a scheme other than http/https. This is only supported on Android and will still default to http,https if not set.
## \[2.2.1]
### bug
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "tauri-plugin-deep-link"
version = "2.2.1"
version = "2.4.0"
description = "Set your Tauri application as the default handler for an URL"
authors = { workspace = true }
license = { workspace = true }
+1 -1
View File
@@ -1 +1 @@
if("__TAURI__"in window){var __TAURI_PLUGIN_DEEP_LINK__=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={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)))}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_ENTER="tauri://drag-enter",e.DRAG_OVER="tauri://drag-over",e.DRAG_DROP="tauri://drag-drop",e.DRAG_LEAVE="tauri://drag-leave"}(t||(t={})),e.getCurrent=async function(){return await r("plugin:deep-link|get_current")},e.isRegistered=async function(e){return await r("plugin:deep-link|is_registered",{protocol:e})},e.onOpenUrl=async function(e){return 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_DEEP_LINK__})}
if("__TAURI__"in window){var __TAURI_PLUGIN_DEEP_LINK__=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 _={kind:"Any"};return r("plugin:event|listen",{event:e,target:_,handler:n(t)}).then((n=>async()=>async function(e,n){window.__TAURI_EVENT_PLUGIN_INTERNALS__.unregisterListener(e,n),await r("plugin:event|unlisten",{event:e,eventId:n})}(e,n)))}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_ENTER="tauri://drag-enter",e.DRAG_OVER="tauri://drag-over",e.DRAG_DROP="tauri://drag-drop",e.DRAG_LEAVE="tauri://drag-leave"}(t||(t={})),e.getCurrent=async function(){return await r("plugin:deep-link|get_current")},e.isRegistered=async function(e){return await r("plugin:deep-link|is_registered",{protocol:e})},e.onOpenUrl=async function(e){return 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_DEEP_LINK__})}
+29 -4
View File
@@ -9,25 +9,50 @@ use config::{AssociatedDomain, Config};
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: Do we want to support the other path* configs too?
fn intent_filter(domain: &AssociatedDomain) -> String {
format!(
r#"<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="http" />
<data android:scheme="https" />
{}
<data android:host="{}" />
{}
{}
{}
{}
</intent-filter>"#,
domain
.scheme
.iter()
.map(|scheme| format!(r#"<data android:scheme="{scheme}" />"#))
.collect::<Vec<_>>()
.join("\n "),
domain.host,
domain
.path
.iter()
.map(|path| format!(r#"<data android:path="{path}" />"#))
.collect::<Vec<_>>()
.join("\n "),
domain
.path_pattern
.iter()
.map(|pattern| format!(r#"<data android:pathPattern="{pattern}" />"#))
.collect::<Vec<_>>()
.join("\n "),
domain
.path_prefix
.iter()
.map(|prefix| format!(r#"<data android:pathPrefix="{prefix}" />"#))
.collect::<Vec<_>>()
.join("\n ")
.join("\n "),
domain
.path_suffix
.iter()
.map(|suffix| format!(r#"<data android:pathSuffix="{suffix}" />"#))
.collect::<Vec<_>>()
.join("\n "),
)
}
@@ -1,5 +1,17 @@
# Changelog
## \[2.2.3]
### Dependencies
- Upgraded to `deep-link-js@2.4.0`
## \[2.2.2]
### Dependencies
- Upgraded to `deep-link-js@2.3.0`
## \[2.2.1]
### Dependencies
+4 -4
View File
@@ -1,7 +1,7 @@
{
"name": "deep-link-example",
"private": true,
"version": "2.2.1",
"version": "2.2.3",
"type": "module",
"scripts": {
"dev": "vite",
@@ -10,11 +10,11 @@
"tauri": "tauri"
},
"dependencies": {
"@tauri-apps/api": "2.4.1",
"@tauri-apps/plugin-deep-link": "2.2.1"
"@tauri-apps/api": "2.6.0",
"@tauri-apps/plugin-deep-link": "2.4.0"
},
"devDependencies": {
"@tauri-apps/cli": "2.4.1",
"@tauri-apps/cli": "2.6.2",
"typescript": "^5.7.3",
"vite": "^6.2.6"
}
@@ -19,7 +19,7 @@ tauri-build = { workspace = true }
[dependencies]
serde = { workspace = true }
serde_json = { workspace = true }
tauri = { workspace = true, features = ["wry", "compression"] }
tauri = { workspace = true, features = ["wry", "common-controls-v6", "x11"] }
tauri-plugin-deep-link = { path = "../../../" }
tauri-plugin-log = { path = "../../../../log" }
tauri-plugin-single-instance = { path = "../../../../single-instance", features = [
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@tauri-apps/plugin-deep-link",
"version": "2.2.1",
"version": "2.4.0",
"description": "Set your Tauri application as the default handler for an URL",
"license": "MIT OR Apache-2.0",
"authors": [
@@ -25,6 +25,6 @@
"LICENSE"
],
"dependencies": {
"@tauri-apps/api": "^2.0.0"
"@tauri-apps/api": "^2.6.0"
}
}
+15
View File
@@ -9,10 +9,25 @@ use tauri_utils::config::DeepLinkProtocol;
#[derive(Deserialize, Clone)]
pub struct AssociatedDomain {
#[serde(default = "default_schemes")]
pub scheme: Vec<String>,
#[serde(deserialize_with = "deserialize_associated_host")]
pub host: String,
#[serde(default)]
pub path: Vec<String>,
#[serde(default, alias = "path-pattern", rename = "pathPattern")]
pub path_pattern: Vec<String>,
#[serde(default, alias = "path-prefix", rename = "pathPrefix")]
pub path_prefix: Vec<String>,
#[serde(default, alias = "path-suffix", rename = "pathSuffix")]
pub path_suffix: Vec<String>,
}
// TODO: Consider removing this in v3
fn default_schemes() -> Vec<String> {
vec!["https".to_string(), "http".to_string()]
}
fn deserialize_associated_host<'de, D>(deserializer: D) -> Result<String, D::Error>
+5 -5
View File
@@ -114,8 +114,8 @@ mod imp {
/// ## Platform-specific:
///
/// - **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.
/// 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.plugin_handle
.run_mobile_plugin::<GetCurrentResponse>("getCurrent", ())
@@ -226,8 +226,8 @@ mod imp {
/// ## Platform-specific:
///
/// - **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.
/// 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>>> {
return Ok(self.current.lock().unwrap().clone());
}
@@ -350,7 +350,7 @@ mod imp {
/// ## Platform-specific:
///
/// - **Windows**: Requires admin rights if the protocol is registered on local machine
/// (this can happen when registered from the NSIS installer when the install mode is set to both or per machine)
/// (this can happen when registered from the NSIS installer when the install mode is set to both or per machine)
/// - **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<()> {
+14
View File
@@ -1,5 +1,19 @@
# Changelog
## \[2.3.0]
- [`f209b2f2`](https://github.com/tauri-apps/plugins-workspace/commit/f209b2f23cb29133c97ad5961fb46ef794dbe063) ([#2804](https://github.com/tauri-apps/plugins-workspace/pull/2804) by [@renovate](https://github.com/tauri-apps/plugins-workspace/../../renovate)) Updated tauri to 2.6
### Dependencies
- Upgraded to `fs-js@2.4.0`
## \[2.2.2]
### Dependencies
- Upgraded to `fs-js@2.3.0`
## \[2.2.1]
### Dependencies
+2 -2
View File
@@ -1,6 +1,6 @@
[package]
name = "tauri-plugin-dialog"
version = "2.2.1"
version = "2.3.0"
description = "Native system dialogs for opening and saving files along with message dialogs on your Tauri application."
edition = { workspace = true }
authors = { workspace = true }
@@ -34,7 +34,7 @@ tauri = { workspace = true }
log = { workspace = true }
thiserror = { workspace = true }
url = { workspace = true }
tauri-plugin-fs = { path = "../fs", version = "2.2.1" }
tauri-plugin-fs = { path = "../fs", version = "2.4.0" }
[target.'cfg(target_os = "ios")'.dependencies]
tauri = { workspace = true, features = ["wry"] }
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@tauri-apps/plugin-dialog",
"version": "2.2.1",
"version": "2.3.0",
"license": "MIT OR Apache-2.0",
"authors": [
"Tauri Programme within The Commons Conservancy"
@@ -24,6 +24,6 @@
"LICENSE"
],
"dependencies": {
"@tauri-apps/api": "^2.0.0"
"@tauri-apps/api": "^2.6.0"
}
}
@@ -7,9 +7,6 @@ available from the dialog plugin.
All dialog types are enabled.
#### This default permission set includes the following:
- `allow-ask`
+8
View File
@@ -1,5 +1,13 @@
# Changelog
## \[2.4.0]
- [`f209b2f2`](https://github.com/tauri-apps/plugins-workspace/commit/f209b2f23cb29133c97ad5961fb46ef794dbe063) ([#2804](https://github.com/tauri-apps/plugins-workspace/pull/2804) by [@renovate](https://github.com/tauri-apps/plugins-workspace/../../renovate)) Updated tauri to 2.6
## \[2.3.0]
- [`dac4d537`](https://github.com/tauri-apps/plugins-workspace/commit/dac4d53724bb3430a00a3f0119857cba32a031e8) ([#2613](https://github.com/tauri-apps/plugins-workspace/pull/2613) by [@Legend-Master](https://github.com/tauri-apps/plugins-workspace/../../Legend-Master)) Reduce the overhead of `watch` and `unwatch`
## \[2.2.1]
### bug
+1 -2
View File
@@ -1,6 +1,6 @@
[package]
name = "tauri-plugin-fs"
version = "2.2.1"
version = "2.4.0"
description = "Access the file system."
authors = { workspace = true }
license = { workspace = true }
@@ -35,7 +35,6 @@ tauri = { workspace = true }
thiserror = { workspace = true }
url = { workspace = true }
anyhow = "1"
uuid = { version = "1", features = ["v4"] }
glob = { workspace = true }
# TODO: Remove `serialization-compat-6` in v3
notify = { version = "8", optional = true, features = [
+2 -2
View File
@@ -68,9 +68,9 @@ fn main() {
Afterwards all the plugin's APIs are available through the JavaScript guest bindings:
```javascript
import { metadata } from '@tauri-apps/plugin-fs'
import { stat } from '@tauri-apps/plugin-fs'
await metadata('/path/to/file')
await stat('/path/to/file')
```
## Contributing
File diff suppressed because one or more lines are too long
+1
View File
@@ -101,6 +101,7 @@ const COMMANDS: &[(&str, &[&str])] = &[
("fstat", &[]),
("exists", &[]),
("watch", &[]),
// TODO: Remove this in v3
("unwatch", &[]),
("size", &[]),
];
+35 -49
View File
@@ -1245,15 +1245,44 @@ type WatchEventKindRemove =
| { kind: 'folder' }
| { kind: 'other' }
// TODO: Remove this in v3, return `Watcher` instead
/**
* @since 2.0.0
*/
type UnwatchFn = () => void
async function unwatch(rid: number): Promise<void> {
await invoke('plugin:fs|unwatch', { rid })
class Watcher extends Resource {}
async function watchInternal(
paths: string | string[] | URL | URL[],
cb: (event: WatchEvent) => void,
options: DebouncedWatchOptions
): Promise<UnwatchFn> {
const watchPaths = Array.isArray(paths) ? paths : [paths]
for (const path of watchPaths) {
if (path instanceof URL && path.protocol !== 'file:') {
throw new TypeError('Must be a file URL.')
}
}
const onEvent = new Channel<WatchEvent>()
onEvent.onmessage = cb
const rid: number = await invoke('plugin:fs|watch', {
paths: watchPaths.map((p) => (p instanceof URL ? p.toString() : p)),
options,
onEvent
})
const watcher = new Watcher(rid)
return () => {
void watcher.close()
}
}
// TODO: Return `Watcher` instead in v3
/**
* Watch changes (after a delay) on files or directories.
*
@@ -1264,34 +1293,13 @@ async function watch(
cb: (event: WatchEvent) => void,
options?: DebouncedWatchOptions
): Promise<UnwatchFn> {
const opts = {
recursive: false,
return await watchInternal(paths, cb, {
delayMs: 2000,
...options
}
const watchPaths = Array.isArray(paths) ? paths : [paths]
for (const path of watchPaths) {
if (path instanceof URL && path.protocol !== 'file:') {
throw new TypeError('Must be a file URL.')
}
}
const onEvent = new Channel<WatchEvent>()
onEvent.onmessage = cb
const rid: number = await invoke('plugin:fs|watch', {
paths: watchPaths.map((p) => (p instanceof URL ? p.toString() : p)),
options: opts,
onEvent
})
return () => {
void unwatch(rid)
}
}
// TODO: Return `Watcher` instead in v3
/**
* Watch changes on files or directories.
*
@@ -1302,32 +1310,10 @@ async function watchImmediate(
cb: (event: WatchEvent) => void,
options?: WatchOptions
): Promise<UnwatchFn> {
const opts = {
recursive: false,
return await watchInternal(paths, cb, {
...options,
delayMs: null
}
const watchPaths = Array.isArray(paths) ? paths : [paths]
for (const path of watchPaths) {
if (path instanceof URL && path.protocol !== 'file:') {
throw new TypeError('Must be a file URL.')
}
}
const onEvent = new Channel<WatchEvent>()
onEvent.onmessage = cb
const rid: number = await invoke('plugin:fs|watch', {
paths: watchPaths.map((p) => (p instanceof URL ? p.toString() : p)),
options: opts,
onEvent
delayMs: undefined
})
return () => {
void unwatch(rid)
}
}
/**
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@tauri-apps/plugin-fs",
"version": "2.2.1",
"version": "2.4.0",
"description": "Access the file system.",
"license": "MIT OR Apache-2.0",
"authors": [
@@ -25,6 +25,6 @@
"LICENSE"
],
"dependencies": {
"@tauri-apps/api": "^2.0.0"
"@tauri-apps/api": "^2.6.0"
}
}
@@ -24,7 +24,6 @@ This default permission set prevents access to critical components
of the Tauri application by default.
On Windows the webview data folder access is denied.
#### This default permission set includes the following:
- `create-app-specific-dirs`
-5
View File
@@ -150,11 +150,6 @@ pub fn open<R: Runtime>(
Ok(rid)
}
#[tauri::command]
pub fn close<R: Runtime>(webview: Webview<R>, rid: ResourceId) -> CommandResult<()> {
webview.resources_table().close(rid).map_err(Into::into)
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CopyFileOptions {
-3
View File
@@ -397,7 +397,6 @@ pub fn init<R: Runtime>() -> TauriPlugin<R, Option<config::Config>> {
commands::create,
commands::open,
commands::copy_file,
commands::close,
commands::mkdir,
commands::read_dir,
commands::read,
@@ -420,8 +419,6 @@ pub fn init<R: Runtime>() -> TauriPlugin<R, Option<config::Config>> {
commands::size,
#[cfg(feature = "watch")]
watcher::watch,
#[cfg(feature = "watch")]
watcher::unwatch
])
.setup(|app, api| {
let scope = Scope {
+43 -98
View File
@@ -2,8 +2,8 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
use notify::{Config, Event, RecommendedWatcher, RecursiveMode, Watcher};
use notify_debouncer_full::{new_debouncer, DebounceEventResult, Debouncer, RecommendedCache};
use notify::{Config, RecommendedWatcher, RecursiveMode, Watcher};
use notify_debouncer_full::{new_debouncer, DebouncedEvent, Debouncer, RecommendedCache};
use serde::Deserialize;
use tauri::{
ipc::{Channel, CommandScope, GlobalScope},
@@ -11,15 +11,7 @@ use tauri::{
Manager, Resource, ResourceId, Runtime, Webview,
};
use std::{
path::PathBuf,
sync::{
mpsc::{channel, Receiver},
Mutex,
},
thread::spawn,
time::Duration,
};
use std::time::Duration;
use crate::{
commands::{resolve_path, CommandResult},
@@ -27,79 +19,44 @@ use crate::{
SafeFilePath,
};
struct InnerWatcher {
pub kind: WatcherKind,
paths: Vec<PathBuf>,
}
pub struct WatcherResource(Mutex<InnerWatcher>);
impl WatcherResource {
fn new(kind: WatcherKind, paths: Vec<PathBuf>) -> Self {
Self(Mutex::new(InnerWatcher { kind, paths }))
}
fn with_lock<R, F: FnMut(&mut InnerWatcher) -> R>(&self, mut f: F) -> R {
let mut watcher = self.0.lock().unwrap();
f(&mut watcher)
}
}
impl Resource for WatcherResource {}
#[allow(unused)]
enum WatcherKind {
Debouncer(Debouncer<RecommendedWatcher, RecommendedCache>),
Watcher(RecommendedWatcher),
}
fn watch_raw(on_event: Channel<Event>, rx: Receiver<notify::Result<Event>>) {
spawn(move || {
while let Ok(event) = rx.recv() {
if let Ok(event) = event {
// TODO: Should errors be emitted too?
let _ = on_event.send(event);
}
}
});
}
fn watch_debounced(on_event: Channel<Event>, rx: Receiver<DebounceEventResult>) {
spawn(move || {
while let Ok(Ok(events)) = rx.recv() {
for event in events {
// TODO: Should errors be emitted too?
let _ = on_event.send(event.event);
}
}
});
}
impl Resource for WatcherKind {}
#[derive(Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct WatchOptions {
base_dir: Option<BaseDirectory>,
#[serde(default)]
recursive: bool,
delay_ms: Option<u64>,
}
#[tauri::command]
pub async fn watch<R: Runtime>(
pub fn watch<R: Runtime>(
webview: Webview<R>,
paths: Vec<SafeFilePath>,
options: WatchOptions,
on_event: Channel<Event>,
on_event: Channel<notify::Event>,
global_scope: GlobalScope<Entry>,
command_scope: CommandScope<Entry>,
) -> CommandResult<ResourceId> {
let mut resolved_paths = Vec::with_capacity(paths.capacity());
for path in paths {
resolved_paths.push(resolve_path(
&webview,
&global_scope,
&command_scope,
path,
options.base_dir,
)?);
}
let resolved_paths = paths
.into_iter()
.map(|path| {
resolve_path(
&webview,
&global_scope,
&command_scope,
path,
options.base_dir,
)
})
.collect::<CommandResult<Vec<_>>>()?;
let recursive_mode = if options.recursive {
RecursiveMode::Recursive
@@ -107,52 +64,40 @@ pub async fn watch<R: Runtime>(
RecursiveMode::NonRecursive
};
let kind = if let Some(delay) = options.delay_ms {
let (tx, rx) = channel();
let mut debouncer = new_debouncer(Duration::from_millis(delay), None, tx)?;
let watcher_kind = if let Some(delay) = options.delay_ms {
let mut debouncer = new_debouncer(
Duration::from_millis(delay),
None,
move |events: Result<Vec<DebouncedEvent>, Vec<notify::Error>>| {
if let Ok(events) = events {
for event in events {
// TODO: Should errors be emitted too?
let _ = on_event.send(event.event);
}
}
},
)?;
for path in &resolved_paths {
debouncer.watch(path, recursive_mode)?;
}
watch_debounced(on_event, rx);
WatcherKind::Debouncer(debouncer)
} else {
let (tx, rx) = channel();
let mut watcher = RecommendedWatcher::new(tx, Config::default())?;
let mut watcher = RecommendedWatcher::new(
move |event| {
if let Ok(event) = event {
// TODO: Should errors be emitted too?
let _ = on_event.send(event);
}
},
Config::default(),
)?;
for path in &resolved_paths {
watcher.watch(path, recursive_mode)?;
}
watch_raw(on_event, rx);
WatcherKind::Watcher(watcher)
};
let rid = webview
.resources_table()
.add(WatcherResource::new(kind, resolved_paths));
let rid = webview.resources_table().add(watcher_kind);
Ok(rid)
}
#[tauri::command]
pub async fn unwatch<R: Runtime>(webview: Webview<R>, rid: ResourceId) -> CommandResult<()> {
let watcher = webview.resources_table().take::<WatcherResource>(rid)?;
WatcherResource::with_lock(&watcher, |watcher| {
match &mut watcher.kind {
WatcherKind::Debouncer(ref mut debouncer) => {
for path in &watcher.paths {
debouncer.unwatch(path).map_err(|e| {
format!("failed to unwatch path: {} with error: {e}", path.display())
})?;
}
}
WatcherKind::Watcher(ref mut w) => {
for path in &watcher.paths {
w.unwatch(path).map_err(|e| {
format!("failed to unwatch path: {} with error: {e}", path.display())
})?;
}
}
}
Ok(())
})
}
+8
View File
@@ -1,5 +1,13 @@
# Changelog
## \[2.3.0]
- [`f209b2f2`](https://github.com/tauri-apps/plugins-workspace/commit/f209b2f23cb29133c97ad5961fb46ef794dbe063) ([#2804](https://github.com/tauri-apps/plugins-workspace/pull/2804) by [@renovate](https://github.com/tauri-apps/plugins-workspace/../../renovate)) Updated tauri to 2.6
## \[2.2.5]
- [`f634e524`](https://github.com/tauri-apps/plugins-workspace/commit/f634e5248ebe428f8305a59f74c13fc15147fb8e) This is an "empty" release to update the plugins' source files on crates.io and docs.rs. This should fix docs.rs build failures for projects using tauri plugins as dependencies.
## \[2.2.4]
- [`a1b3fa27`](https://github.com/tauri-apps/plugins-workspace/commit/a1b3fa27f11022c9b6622b4fab12d93239eb05de) ([#2515](https://github.com/tauri-apps/plugins-workspace/pull/2515) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) Re-exported the `Geolocation`, `Haptics`, `Notification`, and `Os` structs so that they show up on docs.rs.
+1 -1
View File
@@ -1,7 +1,7 @@
[package]
name = "tauri-plugin-geolocation"
description = "Get and track the device's current position"
version = "2.2.4"
version = "2.3.0"
edition = { workspace = true }
authors = { workspace = true }
license = { workspace = true }
+1 -1
View File
@@ -72,7 +72,7 @@ This plugin automatically adds the following permissions to your `AndroidManifes
If your app requires GPS functionality to function, **you** should add the following to your `AndroidManifest.xml` file:
```xml
<uses-feature android:name="android.hardware.gps" android:required="true" />
<uses-feature android:name="android.hardware.location.gps" android:required="true" />
```
The Google Play Store uses this property to decide whether it should show the app to devices without GPS capabilities.
+1 -1
View File
@@ -1 +1 @@
if("__TAURI__"in window){var __TAURI_PLUGIN_GEOLOCATION__=function(t){"use strict";function n(t,n,e,i){if("function"==typeof n||!n.has(t))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===e?i:"a"===e?i.call(t):i?i.value:n.get(t)}function e(t,n,e,i,s){if("function"==typeof n||!n.has(t))throw new TypeError("Cannot write private member to an object whose class did not declare it");return n.set(t,e),e}var i,s,o;"function"==typeof SuppressedError&&SuppressedError;const r="__TAURI_TO_IPC_KEY__";class a{constructor(){this.__TAURI_CHANNEL_MARKER__=!0,i.set(this,(()=>{})),s.set(this,0),o.set(this,[]),this.id=function(t,n=!1){return window.__TAURI_INTERNALS__.transformCallback(t,n)}((({message:t,id:r})=>{if(r==n(this,s,"f"))for(n(this,i,"f").call(this,t),e(this,s,n(this,s,"f")+1);n(this,s,"f")in n(this,o,"f");){const t=n(this,o,"f")[n(this,s,"f")];n(this,i,"f").call(this,t),delete n(this,o,"f")[n(this,s,"f")],e(this,s,n(this,s,"f")+1)}else n(this,o,"f")[r]=t}))}set onmessage(t){e(this,i,t)}get onmessage(){return n(this,i,"f")}[(i=new WeakMap,s=new WeakMap,o=new WeakMap,r)](){return`__CHANNEL__:${this.id}`}toJSON(){return this[r]()}}async function c(t,n={},e){return window.__TAURI_INTERNALS__.invoke(t,n,e)}return t.checkPermissions=async function(){return await async function(t){return c(`plugin:${t}|check_permissions`)}("geolocation")},t.clearWatch=async function(t){await c("plugin:geolocation|clear_watch",{channelId:t})},t.getCurrentPosition=async function(t){return await c("plugin:geolocation|get_current_position",{options:t})},t.requestPermissions=async function(t){return await c("plugin:geolocation|request_permissions",{permissions:t})},t.watchPosition=async function(t,n){const e=new a;return e.onmessage=t=>{"string"==typeof t?n(null,t):n(t)},await c("plugin:geolocation|watch_position",{options:t,channel:e}),e.id},t}({});Object.defineProperty(window.__TAURI__,"geolocation",{value:__TAURI_PLUGIN_GEOLOCATION__})}
if("__TAURI__"in window){var __TAURI_PLUGIN_GEOLOCATION__=function(t){"use strict";function n(t,n,i,e){if("function"==typeof n?t!==n||!e:!n.has(t))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===i?e:"a"===i?e.call(t):e?e.value:n.get(t)}function i(t,n,i,e,s){if("function"==typeof n||!n.has(t))throw new TypeError("Cannot write private member to an object whose class did not declare it");return n.set(t,i),i}var e,s,o,a;"function"==typeof SuppressedError&&SuppressedError;const r="__TAURI_TO_IPC_KEY__";class c{constructor(t){e.set(this,void 0),s.set(this,0),o.set(this,[]),a.set(this,void 0),i(this,e,t||(()=>{})),this.id=function(t,n=!1){return window.__TAURI_INTERNALS__.transformCallback(t,n)}((t=>{const r=t.index;if("end"in t)return void(r==n(this,s,"f")?this.cleanupCallback():i(this,a,r));const c=t.message;if(r==n(this,s,"f")){for(n(this,e,"f").call(this,c),i(this,s,n(this,s,"f")+1);n(this,s,"f")in n(this,o,"f");){const t=n(this,o,"f")[n(this,s,"f")];n(this,e,"f").call(this,t),delete n(this,o,"f")[n(this,s,"f")],i(this,s,n(this,s,"f")+1)}n(this,s,"f")===n(this,a,"f")&&this.cleanupCallback()}else n(this,o,"f")[r]=c}))}cleanupCallback(){window.__TAURI_INTERNALS__.unregisterCallback(this.id)}set onmessage(t){i(this,e,t)}get onmessage(){return n(this,e,"f")}[(e=new WeakMap,s=new WeakMap,o=new WeakMap,a=new WeakMap,r)](){return`__CHANNEL__:${this.id}`}toJSON(){return this[r]()}}async function _(t,n={},i){return window.__TAURI_INTERNALS__.invoke(t,n,i)}return t.checkPermissions=async function(){return await async function(t){return _(`plugin:${t}|check_permissions`)}("geolocation")},t.clearWatch=async function(t){await _("plugin:geolocation|clear_watch",{channelId:t})},t.getCurrentPosition=async function(t){return await _("plugin:geolocation|get_current_position",{options:t})},t.requestPermissions=async function(t){return await _("plugin:geolocation|request_permissions",{permissions:t})},t.watchPosition=async function(t,n){const i=new c;return i.onmessage=t=>{"string"==typeof t?n(null,t):n(t)},await _("plugin:geolocation|watch_position",{options:t,channel:i}),i.id},t}({});Object.defineProperty(window.__TAURI__,"geolocation",{value:__TAURI_PLUGIN_GEOLOCATION__})}
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@tauri-apps/plugin-geolocation",
"version": "2.2.4",
"version": "2.3.0",
"license": "MIT OR Apache-2.0",
"authors": [
"Tauri Programme within The Commons Conservancy"
@@ -24,6 +24,6 @@
"LICENSE"
],
"dependencies": {
"@tauri-apps/api": "^2.0.0"
"@tauri-apps/api": "^2.6.0"
}
}
@@ -1,4 +1,3 @@
## Permission Table
<table>
+8
View File
@@ -1,5 +1,13 @@
# Changelog
## \[2.3.0]
- [`f209b2f2`](https://github.com/tauri-apps/plugins-workspace/commit/f209b2f23cb29133c97ad5961fb46ef794dbe063) ([#2804](https://github.com/tauri-apps/plugins-workspace/pull/2804) by [@renovate](https://github.com/tauri-apps/plugins-workspace/../../renovate)) Updated tauri to 2.6
## \[2.2.1]
- [`494d1fea`](https://github.com/tauri-apps/plugins-workspace/commit/494d1fea137ffd60da98b25305c9d666df62cc63) ([#2684](https://github.com/tauri-apps/plugins-workspace/pull/2684) by [@renovate](https://github.com/tauri-apps/plugins-workspace/../../renovate)) Updated `global-hotkey` crate to `0.7` to fix a panic when trying to register a key combination which was already registered by another program. No API changes.
## \[2.2.0]
- [`3a79266b`](https://github.com/tauri-apps/plugins-workspace/commit/3a79266b8cf96a55b1ae6339d725567d45a44b1d) ([#2173](https://github.com/tauri-apps/plugins-workspace/pull/2173) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) Bumped all plugins to `v2.2.0`. From now, the versions for the Rust and JavaScript packages of each plugin will be in sync with each other.
+2 -2
View File
@@ -1,6 +1,6 @@
[package]
name = "tauri-plugin-global-shortcut"
version = "2.2.0"
version = "2.3.0"
description = "Register global hotkeys listeners on your Tauri application."
edition = { workspace = true }
authors = { workspace = true }
@@ -31,4 +31,4 @@ log = { workspace = true }
thiserror = { workspace = true }
[target."cfg(not(any(target_os = \"android\", target_os = \"ios\")))".dependencies]
global-hotkey = { version = "0.6", features = ["serde"] }
global-hotkey = { version = "0.7", features = ["serde"] }
+1 -1
View File
@@ -1 +1 @@
if("__TAURI__"in window){var __TAURI_PLUGIN_GLOBAL_SHORTCUT__=function(t){"use strict";function e(t,e,r,s){if("function"==typeof e||!e.has(t))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===r?s:"a"===r?s.call(t):s?s.value:e.get(t)}function r(t,e,r,s,i){if("function"==typeof e||!e.has(t))throw new TypeError("Cannot write private member to an object whose class did not declare it");return e.set(t,r),r}var s,i,n;"function"==typeof SuppressedError&&SuppressedError;const o="__TAURI_TO_IPC_KEY__";class a{constructor(){this.__TAURI_CHANNEL_MARKER__=!0,s.set(this,(()=>{})),i.set(this,0),n.set(this,[]),this.id=function(t,e=!1){return window.__TAURI_INTERNALS__.transformCallback(t,e)}((({message:t,id:o})=>{if(o==e(this,i,"f"))for(e(this,s,"f").call(this,t),r(this,i,e(this,i,"f")+1);e(this,i,"f")in e(this,n,"f");){const t=e(this,n,"f")[e(this,i,"f")];e(this,s,"f").call(this,t),delete e(this,n,"f")[e(this,i,"f")],r(this,i,e(this,i,"f")+1)}else e(this,n,"f")[o]=t}))}set onmessage(t){r(this,s,t)}get onmessage(){return e(this,s,"f")}[(s=new WeakMap,i=new WeakMap,n=new WeakMap,o)](){return`__CHANNEL__:${this.id}`}toJSON(){return this[o]()}}async function _(t,e={},r){return window.__TAURI_INTERNALS__.invoke(t,e,r)}return t.isRegistered=async function(t){return await _("plugin:global-shortcut|is_registered",{shortcut:t})},t.register=async function(t,e){const r=new a;return r.onmessage=e,await _("plugin:global-shortcut|register",{shortcuts:Array.isArray(t)?t:[t],handler:r})},t.unregister=async function(t){return await _("plugin:global-shortcut|unregister",{shortcuts:Array.isArray(t)?t:[t]})},t.unregisterAll=async function(){return await _("plugin:global-shortcut|unregister_all",{})},t}({});Object.defineProperty(window.__TAURI__,"globalShortcut",{value:__TAURI_PLUGIN_GLOBAL_SHORTCUT__})}
if("__TAURI__"in window){var __TAURI_PLUGIN_GLOBAL_SHORTCUT__=function(t){"use strict";function e(t,e,s,i){if("function"==typeof e?t!==e||!i:!e.has(t))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===s?i:"a"===s?i.call(t):i?i.value:e.get(t)}function s(t,e,s,i,r){if("function"==typeof e||!e.has(t))throw new TypeError("Cannot write private member to an object whose class did not declare it");return e.set(t,s),s}var i,r,n,a;"function"==typeof SuppressedError&&SuppressedError;const o="__TAURI_TO_IPC_KEY__";class c{constructor(t){i.set(this,void 0),r.set(this,0),n.set(this,[]),a.set(this,void 0),s(this,i,t||(()=>{})),this.id=function(t,e=!1){return window.__TAURI_INTERNALS__.transformCallback(t,e)}((t=>{const o=t.index;if("end"in t)return void(o==e(this,r,"f")?this.cleanupCallback():s(this,a,o));const c=t.message;if(o==e(this,r,"f")){for(e(this,i,"f").call(this,c),s(this,r,e(this,r,"f")+1);e(this,r,"f")in e(this,n,"f");){const t=e(this,n,"f")[e(this,r,"f")];e(this,i,"f").call(this,t),delete e(this,n,"f")[e(this,r,"f")],s(this,r,e(this,r,"f")+1)}e(this,r,"f")===e(this,a,"f")&&this.cleanupCallback()}else e(this,n,"f")[o]=c}))}cleanupCallback(){window.__TAURI_INTERNALS__.unregisterCallback(this.id)}set onmessage(t){s(this,i,t)}get onmessage(){return e(this,i,"f")}[(i=new WeakMap,r=new WeakMap,n=new WeakMap,a=new WeakMap,o)](){return`__CHANNEL__:${this.id}`}toJSON(){return this[o]()}}async function u(t,e={},s){return window.__TAURI_INTERNALS__.invoke(t,e,s)}return t.isRegistered=async function(t){return await u("plugin:global-shortcut|is_registered",{shortcut:t})},t.register=async function(t,e){const s=new c;return s.onmessage=e,await u("plugin:global-shortcut|register",{shortcuts:Array.isArray(t)?t:[t],handler:s})},t.unregister=async function(t){return await u("plugin:global-shortcut|unregister",{shortcuts:Array.isArray(t)?t:[t]})},t.unregisterAll=async function(){return await u("plugin:global-shortcut|unregister_all",{})},t}({});Object.defineProperty(window.__TAURI__,"globalShortcut",{value:__TAURI_PLUGIN_GLOBAL_SHORTCUT__})}
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@tauri-apps/plugin-global-shortcut",
"version": "2.2.0",
"version": "2.3.0",
"license": "MIT OR Apache-2.0",
"authors": [
"Tauri Programme within The Commons Conservancy"
@@ -24,6 +24,6 @@
"LICENSE"
],
"dependencies": {
"@tauri-apps/api": "^2.0.0"
"@tauri-apps/api": "^2.6.0"
}
}
@@ -5,10 +5,6 @@ the shortcuts can be inherently dangerous and it is
application specific if specific shortcuts should be
registered or unregistered.
#### This default permission set includes the following:
## Permission Table
<table>
+8
View File
@@ -1,5 +1,13 @@
# Changelog
## \[2.3.0]
- [`f209b2f2`](https://github.com/tauri-apps/plugins-workspace/commit/f209b2f23cb29133c97ad5961fb46ef794dbe063) ([#2804](https://github.com/tauri-apps/plugins-workspace/pull/2804) by [@renovate](https://github.com/tauri-apps/plugins-workspace/../../renovate)) Updated tauri to 2.6
## \[2.2.5]
- [`f634e524`](https://github.com/tauri-apps/plugins-workspace/commit/f634e5248ebe428f8305a59f74c13fc15147fb8e) This is an "empty" release to update the plugins' source files on crates.io and docs.rs. This should fix docs.rs build failures for projects using tauri plugins as dependencies.
## \[2.2.4]
- [`a1b3fa27`](https://github.com/tauri-apps/plugins-workspace/commit/a1b3fa27f11022c9b6622b4fab12d93239eb05de) ([#2515](https://github.com/tauri-apps/plugins-workspace/pull/2515) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) Re-exported the `Geolocation`, `Haptics`, `Notification`, and `Os` structs so that they show up on docs.rs.
+1 -1
View File
@@ -1,7 +1,7 @@
[package]
name = "tauri-plugin-haptics"
description = "Haptic feedback and vibrations on Android and iOS"
version = "2.2.4"
version = "2.3.0"
edition = { workspace = true }
authors = { workspace = true }
license = { workspace = true }
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@tauri-apps/plugin-haptics",
"version": "2.2.4",
"version": "2.3.0",
"license": "MIT OR Apache-2.0",
"authors": [
"Tauri Programme within The Commons Conservancy"
@@ -24,6 +24,6 @@
"LICENSE"
],
"dependencies": {
"@tauri-apps/api": "^2.0.0"
"@tauri-apps/api": "^2.6.0"
}
}
@@ -1,4 +1,3 @@
## Permission Table
<table>
+16
View File
@@ -1,5 +1,21 @@
# Changelog
## \[2.5.0]
- [`f209b2f2`](https://github.com/tauri-apps/plugins-workspace/commit/f209b2f23cb29133c97ad5961fb46ef794dbe063) ([#2804](https://github.com/tauri-apps/plugins-workspace/pull/2804) by [@renovate](https://github.com/tauri-apps/plugins-workspace/../../renovate)) Updated tauri to 2.6
### Dependencies
- Upgraded to `fs-js@2.4.0`
## \[2.4.4]
- [`ff384cba`](https://github.com/tauri-apps/plugins-workspace/commit/ff384cbabe82ae715798a4ee49fd07ffcfbcdb5d) ([#2636](https://github.com/tauri-apps/plugins-workspace/pull/2636) by [@asomethings](https://github.com/tauri-apps/plugins-workspace/../../asomethings)) Properly handle responses with status code 204.
### Dependencies
- Upgraded to `fs-js@2.3.0`
## \[2.4.3]
- [`37c0477a`](https://github.com/tauri-apps/plugins-workspace/commit/37c0477afe926d326573f1827045875ce8bf8187) ([#2561](https://github.com/tauri-apps/plugins-workspace/pull/2561)) Add `zstd` cargo feature flag to enable `reqwest/zstd` flag.
+2 -2
View File
@@ -1,6 +1,6 @@
[package]
name = "tauri-plugin-http"
version = "2.4.3"
version = "2.5.0"
description = "Access an HTTP client written in Rust."
edition = { workspace = true }
authors = { workspace = true }
@@ -34,7 +34,7 @@ serde_json = { workspace = true }
tauri = { workspace = true }
thiserror = { workspace = true }
tokio = { version = "1", features = ["sync", "macros"] }
tauri-plugin-fs = { path = "../fs", version = "2.2.1" }
tauri-plugin-fs = { path = "../fs", version = "2.4.0" }
urlpattern = "0.3"
regex = "1"
http = "1"
+1 -1
View File
@@ -1 +1 @@
if("__TAURI__"in window){var __TAURI_PLUGIN_HTTP__=function(e){"use strict";function t(e,t,r,n){if("function"==typeof t||!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===r?n:"a"===r?n.call(e):n?n.value:t.get(e)}function r(e,t,r,n,s){if("function"==typeof t||!t.has(e))throw new TypeError("Cannot write private member to an object whose class did not declare it");return t.set(e,r),r}var n,s,a;"function"==typeof SuppressedError&&SuppressedError;const i="__TAURI_TO_IPC_KEY__";class o{constructor(){this.__TAURI_CHANNEL_MARKER__=!0,n.set(this,(()=>{})),s.set(this,0),a.set(this,[]),this.id=function(e,t=!1){return window.__TAURI_INTERNALS__.transformCallback(e,t)}((({message:e,id:i})=>{if(i==t(this,s,"f"))for(t(this,n,"f").call(this,e),r(this,s,t(this,s,"f")+1);t(this,s,"f")in t(this,a,"f");){const e=t(this,a,"f")[t(this,s,"f")];t(this,n,"f").call(this,e),delete t(this,a,"f")[t(this,s,"f")],r(this,s,t(this,s,"f")+1)}else t(this,a,"f")[i]=e}))}set onmessage(e){r(this,n,e)}get onmessage(){return t(this,n,"f")}[(n=new WeakMap,s=new WeakMap,a=new WeakMap,i)](){return`__CHANNEL__:${this.id}`}toJSON(){return this[i]()}}async function c(e,t={},r){return window.__TAURI_INTERNALS__.invoke(e,t,r)}const d="Request cancelled";return e.fetch=async function(e,t){const r=t?.signal;if(r?.aborted)throw new Error(d);const n=t?.maxRedirections,s=t?.connectTimeout,a=t?.proxy,i=t?.danger;t&&(delete t.maxRedirections,delete t.connectTimeout,delete t.proxy,delete t.danger);const h=t?.headers?t.headers instanceof Headers?t.headers:new Headers(t.headers):new Headers,f=new Request(e,t),_=await f.arrayBuffer(),u=0!==_.byteLength?Array.from(new Uint8Array(_)):null;for(const[e,t]of f.headers)h.get(e)||h.set(e,t);const l=(h instanceof Headers?Array.from(h.entries()):Array.isArray(h)?h:Object.entries(h)).map((([e,t])=>[e,"string"==typeof t?t:t.toString()]));if(r?.aborted)throw new Error(d);const w=await c("plugin:http|fetch",{clientConfig:{method:f.method,url:f.url,headers:l,data:u,maxRedirections:n,connectTimeout:s,proxy:a,danger:i}}),p=()=>c("plugin:http|fetch_cancel",{rid:w});if(r?.aborted)throw p(),new Error(d);r?.addEventListener("abort",(()=>{p()}));const{status:y,statusText:m,url:T,headers:g,rid:b}=await c("plugin:http|fetch_send",{rid:w}),A=new ReadableStream({start:e=>{const t=new o;t.onmessage=t=>{if(r?.aborted)return void e.error(d);const n=new Uint8Array(t),s=n[n.byteLength-1],a=n.slice(0,n.byteLength-1);1!=s?e.enqueue(a):e.close()},c("plugin:http|fetch_read_body",{rid:b,streamChannel:t}).catch((t=>{e.error(t)}))}}),R=new Response(A,{status:y,statusText:m});return Object.defineProperty(R,"url",{value:T}),Object.defineProperty(R,"headers",{value:new Headers(g)}),R},e}({});Object.defineProperty(window.__TAURI__,"http",{value:__TAURI_PLUGIN_HTTP__})}
if("__TAURI__"in window){var __TAURI_PLUGIN_HTTP__=function(e){"use strict";function t(e,t,r,n){if("function"==typeof t?e!==t||!n:!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===r?n:"a"===r?n.call(e):n?n.value:t.get(e)}function r(e,t,r,n,s){if("function"==typeof t||!t.has(e))throw new TypeError("Cannot write private member to an object whose class did not declare it");return t.set(e,r),r}var n,s,i,a;"function"==typeof SuppressedError&&SuppressedError;const o="__TAURI_TO_IPC_KEY__";class c{constructor(e){n.set(this,void 0),s.set(this,0),i.set(this,[]),a.set(this,void 0),r(this,n,e||(()=>{})),this.id=function(e,t=!1){return window.__TAURI_INTERNALS__.transformCallback(e,t)}((e=>{const o=e.index;if("end"in e)return void(o==t(this,s,"f")?this.cleanupCallback():r(this,a,o));const c=e.message;if(o==t(this,s,"f")){for(t(this,n,"f").call(this,c),r(this,s,t(this,s,"f")+1);t(this,s,"f")in t(this,i,"f");){const e=t(this,i,"f")[t(this,s,"f")];t(this,n,"f").call(this,e),delete t(this,i,"f")[t(this,s,"f")],r(this,s,t(this,s,"f")+1)}t(this,s,"f")===t(this,a,"f")&&this.cleanupCallback()}else t(this,i,"f")[o]=c}))}cleanupCallback(){window.__TAURI_INTERNALS__.unregisterCallback(this.id)}set onmessage(e){r(this,n,e)}get onmessage(){return t(this,n,"f")}[(n=new WeakMap,s=new WeakMap,i=new WeakMap,a=new WeakMap,o)](){return`__CHANNEL__:${this.id}`}toJSON(){return this[o]()}}async function d(e,t={},r){return window.__TAURI_INTERNALS__.invoke(e,t,r)}const h="Request cancelled";return e.fetch=async function(e,t){const r=t?.signal;if(r?.aborted)throw new Error(h);const n=t?.maxRedirections,s=t?.connectTimeout,i=t?.proxy,a=t?.danger;t&&(delete t.maxRedirections,delete t.connectTimeout,delete t.proxy,delete t.danger);const o=t?.headers?t.headers instanceof Headers?t.headers:new Headers(t.headers):new Headers,f=new Request(e,t),l=await f.arrayBuffer(),u=0!==l.byteLength?Array.from(new Uint8Array(l)):null;for(const[e,t]of f.headers)o.get(e)||o.set(e,t);const _=(o instanceof Headers?Array.from(o.entries()):Array.isArray(o)?o:Object.entries(o)).map((([e,t])=>[e,"string"==typeof t?t:t.toString()]));if(r?.aborted)throw new Error(h);const w=await d("plugin:http|fetch",{clientConfig:{method:f.method,url:f.url,headers:_,data:u,maxRedirections:n,connectTimeout:s,proxy:i,danger:a}}),p=()=>d("plugin:http|fetch_cancel",{rid:w});if(r?.aborted)throw p(),new Error(h);r?.addEventListener("abort",(()=>{p()}));const{status:y,statusText:m,url:b,headers:T,rid:g}=await d("plugin:http|fetch_send",{rid:w}),A=[101,103,204,205,304].includes(y)?null:new ReadableStream({start:e=>{const t=new c;t.onmessage=t=>{if(r?.aborted)return void e.error(h);const n=new Uint8Array(t),s=n[n.byteLength-1],i=n.slice(0,n.byteLength-1);1!=s?e.enqueue(i):e.close()},d("plugin:http|fetch_read_body",{rid:g,streamChannel:t}).catch((t=>{e.error(t)}))}}),R=new Response(A,{status:y,statusText:m});return Object.defineProperty(R,"url",{value:b}),Object.defineProperty(R,"headers",{value:new Headers(T)}),R},e}({});Object.defineProperty(window.__TAURI__,"http",{value:__TAURI_PLUGIN_HTTP__})}
+35 -31
View File
@@ -229,40 +229,44 @@ export async function fetch(
rid
})
const readableStreamBody = new ReadableStream({
start: (controller) => {
const streamChannel = new Channel<ArrayBuffer | number[]>()
streamChannel.onmessage = (res: ArrayBuffer | number[]) => {
// close early if aborted
if (signal?.aborted) {
controller.error(ERROR_REQUEST_CANCELLED)
return
// no body for 101, 103, 204, 205 and 304
// see https://fetch.spec.whatwg.org/#null-body-status
const body = [101, 103, 204, 205, 304].includes(status)
? null
: new ReadableStream({
start: (controller) => {
const streamChannel = new Channel<ArrayBuffer | number[]>()
streamChannel.onmessage = (res: ArrayBuffer | number[]) => {
// close early if aborted
if (signal?.aborted) {
controller.error(ERROR_REQUEST_CANCELLED)
return
}
const resUint8 = new Uint8Array(res)
const lastByte = resUint8[resUint8.byteLength - 1]
const actualRes = resUint8.slice(0, resUint8.byteLength - 1)
// close when the signal to close (last byte is 1) is sent from the IPC.
if (lastByte == 1) {
controller.close()
return
}
controller.enqueue(actualRes)
}
// run a non-blocking body stream fetch
invoke('plugin:http|fetch_read_body', {
rid: responseRid,
streamChannel
}).catch((e) => {
controller.error(e)
})
}
const resUint8 = new Uint8Array(res)
const lastByte = resUint8[resUint8.byteLength - 1]
const actualRes = resUint8.slice(0, resUint8.byteLength - 1)
// close when the signal to close (last byte is 1) is sent from the IPC.
if (lastByte == 1) {
controller.close()
return
}
controller.enqueue(actualRes)
}
// run a non-blocking body stream fetch
invoke('plugin:http|fetch_read_body', {
rid: responseRid,
streamChannel
}).catch((e) => {
controller.error(e)
})
}
})
const res = new Response(readableStreamBody, {
const res = new Response(body, {
status,
statusText
})
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@tauri-apps/plugin-http",
"version": "2.4.3",
"version": "2.5.0",
"license": "MIT OR Apache-2.0",
"authors": [
"Tauri Programme within The Commons Conservancy"
@@ -24,6 +24,6 @@
"LICENSE"
],
"dependencies": {
"@tauri-apps/api": "^2.0.0"
"@tauri-apps/api": "^2.6.0"
}
}
@@ -11,8 +11,6 @@ be manually configured before usage.
All fetch operations are enabled.
#### This default permission set includes the following:
- `allow-fetch`
+8
View File
@@ -1,5 +1,13 @@
# Changelog
## \[2.3.0]
- [`f209b2f2`](https://github.com/tauri-apps/plugins-workspace/commit/f209b2f23cb29133c97ad5961fb46ef794dbe063) ([#2804](https://github.com/tauri-apps/plugins-workspace/pull/2804) by [@renovate](https://github.com/tauri-apps/plugins-workspace/../../renovate)) Updated tauri to 2.6
## \[2.2.1]
- [`f634e524`](https://github.com/tauri-apps/plugins-workspace/commit/f634e5248ebe428f8305a59f74c13fc15147fb8e) This is an "empty" release to update the plugins' source files on crates.io and docs.rs. This should fix docs.rs build failures for projects using tauri plugins as dependencies.
## \[2.2.0]
- [`3a79266b`](https://github.com/tauri-apps/plugins-workspace/commit/3a79266b8cf96a55b1ae6339d725567d45a44b1d) ([#2173](https://github.com/tauri-apps/plugins-workspace/pull/2173) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) Bumped all plugins to `v2.2.0`. From now, the versions for the Rust and JavaScript packages of each plugin will be in sync with each other.
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "tauri-plugin-localhost"
version = "2.2.0"
version = "2.3.0"
description = "Expose your apps assets through a localhost server instead of the default custom protocol."
authors = { workspace = true }
license = { workspace = true }
+24
View File
@@ -1,5 +1,29 @@
# Changelog
## \[2.6.0]
- [`f209b2f2`](https://github.com/tauri-apps/plugins-workspace/commit/f209b2f23cb29133c97ad5961fb46ef794dbe063) ([#2804](https://github.com/tauri-apps/plugins-workspace/pull/2804) by [@renovate](https://github.com/tauri-apps/plugins-workspace/../../renovate)) Updated tauri to 2.6
## \[2.5.1]
### bug
- [`9799f0db`](https://github.com/tauri-apps/plugins-workspace/commit/9799f0dbabea0b572a9b9111954fbf9aca63da71) ([#2802](https://github.com/tauri-apps/plugins-workspace/pull/2802) by [@lucasfernog](https://github.com/tauri-apps/plugins-workspace/../../lucasfernog)) Fixes iOS simulator still freezing sometimes due to early logging.
## \[2.5.0]
- [`106e46ed`](https://github.com/tauri-apps/plugins-workspace/commit/106e46ed5125be33d0427cab9c5c066802f68791) ([#677](https://github.com/tauri-apps/plugins-workspace/pull/677)) Added the `KeepSome` rotation strategy. Like `KeepAll` it will rename files when the max file size is exceeded but will keep only the specified amount of files around.
## \[2.4.0]
- [`c9b21f6f`](https://github.com/tauri-apps/plugins-workspace/commit/c9b21f6f4345806eff5f495885f20dea0082b7d7) ([#2625](https://github.com/tauri-apps/plugins-workspace/pull/2625) by [@lucasfernog](https://github.com/tauri-apps/plugins-workspace/../../lucasfernog)) Export the `LogLevel` type.
- [`9629c2f4`](https://github.com/tauri-apps/plugins-workspace/commit/9629c2f4f90a56b5c2d265d1d13d3af40fc0c525) ([#2600](https://github.com/tauri-apps/plugins-workspace/pull/2600) by [@exoego](https://github.com/tauri-apps/plugins-workspace/../../exoego)) Adds a new varient `TargetKind::Dispatch` that allows you to construct arbitrary log targets
- [`686a839c`](https://github.com/tauri-apps/plugins-workspace/commit/686a839c96fae1b0334f2df9dc76ca5cdbe00dbe) ([#2626](https://github.com/tauri-apps/plugins-workspace/pull/2626) by [@lucasfernog](https://github.com/tauri-apps/plugins-workspace/../../lucasfernog)) Fix iOS app stuck when using the iOS Simulator and the log plugin due to a deadlock when calling os_log too early.
### feat
- [`60fc35d3`](https://github.com/tauri-apps/plugins-workspace/commit/60fc35d35cccaf1654eceb4446ecf0f89dc15502) ([#2576](https://github.com/tauri-apps/plugins-workspace/pull/2576) by [@3lpsy](https://github.com/tauri-apps/plugins-workspace/../../3lpsy)) Add a `tracing` feature to the `log` plugin that emits log messages to the `tracing` system.
## \[2.3.1]
- [`1bb1ced5`](https://github.com/tauri-apps/plugins-workspace/commit/1bb1ced53820127204aa7adf57510c1cbce55e12) ([#2524](https://github.com/tauri-apps/plugins-workspace/pull/2524) by [@elwerene](https://github.com/tauri-apps/plugins-workspace/../../elwerene)) enable TargetKind::LogDir on mobile
+7 -3
View File
@@ -1,6 +1,6 @@
[package]
name = "tauri-plugin-log"
version = "2.3.1"
version = "2.6.0"
description = "Configurable logging for your Tauri app."
authors = { workspace = true }
license = { workspace = true }
@@ -31,11 +31,15 @@ thiserror = { workspace = true }
serde_repr = "0.1"
byte-unit = "5"
log = { workspace = true, features = ["kv_unstable"] }
time = { version = "0.3", features = ["formatting", "local-offset", "macros"] }
time = { version = "0.3", features = [
"formatting",
"local-offset",
"macros",
"parsing",
] }
fern = "0.7"
tracing = { workspace = true, optional = true }
[target."cfg(target_os = \"android\")".dependencies]
android_logger = "0.15"
+1 -1
View File
@@ -1 +1 @@
if("__TAURI__"in window){var __TAURI_PLUGIN_LOG__=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 a,t;async function o(e,a,t){const o={kind:"Any"};return r("plugin:event|listen",{event:e,target:o,handler:n(a)}).then((n=>async()=>async function(e,n){await r("plugin:event|unlisten",{event:e,eventId:n})}(e,n)))}async function i(e,n,a){const t=function(e){if(e){if(!e.startsWith("Error")){const n=e.split("\n").map((e=>e.split("@"))).filter((([e,n])=>e.length>0&&"[native code]"!==n));return n[2]?.filter((e=>e.length>0)).join("@")}{const n=e.split("\n"),r=n[3]?.trim();if(!r)return;const a=/at\s+(?<functionName>.*?)\s+\((?<fileName>.*?):(?<lineNumber>\d+):(?<columnNumber>\d+)\)/,t=r.match(a);if(t){const{functionName:e,fileName:n,lineNumber:r,columnNumber:a}=t.groups;return`${e}@${n}:${r}:${a}`}{const e=/at\s+(?<fileName>.*?):(?<lineNumber>\d+):(?<columnNumber>\d+)/,n=r.match(e);if(n){const{fileName:e,lineNumber:r,columnNumber:a}=n.groups;return`<anonymous>@${e}:${r}:${a}`}}}}}((new Error).stack),{file:o,line:i,keyValues:u}=a??{};await r("plugin:log|log",{level:e,message:n,location:t,file:o,line:i,keyValues:u})}async function u(e){return await o("log://log",(n=>{const{level:r}=n.payload;let{message:a}=n.payload;a=a.replace(/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g,""),e({message:a,level:r})}))}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_ENTER="tauri://drag-enter",e.DRAG_OVER="tauri://drag-over",e.DRAG_DROP="tauri://drag-drop",e.DRAG_LEAVE="tauri://drag-leave"}(a||(a={})),function(e){e[e.Trace=1]="Trace",e[e.Debug=2]="Debug",e[e.Info=3]="Info",e[e.Warn=4]="Warn",e[e.Error=5]="Error"}(t||(t={})),e.attachConsole=async function(){return await u((({level:e,message:n})=>{switch(e){case t.Trace:console.log(n);break;case t.Debug:console.debug(n);break;case t.Info:console.info(n);break;case t.Warn:console.warn(n);break;case t.Error:console.error(n);break;default:throw new Error(`unknown log level ${e}`)}}))},e.attachLogger=u,e.debug=async function(e,n){await i(t.Debug,e,n)},e.error=async function(e,n){await i(t.Error,e,n)},e.info=async function(e,n){await i(t.Info,e,n)},e.trace=async function(e,n){await i(t.Trace,e,n)},e.warn=async function(e,n){await i(t.Warn,e,n)},e}({});Object.defineProperty(window.__TAURI__,"log",{value:__TAURI_PLUGIN_LOG__})}
if("__TAURI__"in window){var __TAURI_PLUGIN_LOG__=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 a,t;async function o(e,a,t){const o={kind:"Any"};return r("plugin:event|listen",{event:e,target:o,handler:n(a)}).then((n=>async()=>async function(e,n){window.__TAURI_EVENT_PLUGIN_INTERNALS__.unregisterListener(e,n),await r("plugin:event|unlisten",{event:e,eventId:n})}(e,n)))}async function i(e,n,a){const t=function(e){if(e){if(!e.startsWith("Error")){const n=e.split("\n").map((e=>e.split("@"))).filter((([e,n])=>e.length>0&&"[native code]"!==n));return n[2]?.filter((e=>e.length>0)).join("@")}{const n=e.split("\n"),r=n[3]?.trim();if(!r)return;const a=/at\s+(?<functionName>.*?)\s+\((?<fileName>.*?):(?<lineNumber>\d+):(?<columnNumber>\d+)\)/,t=r.match(a);if(t){const{functionName:e,fileName:n,lineNumber:r,columnNumber:a}=t.groups;return`${e}@${n}:${r}:${a}`}{const e=/at\s+(?<fileName>.*?):(?<lineNumber>\d+):(?<columnNumber>\d+)/,n=r.match(e);if(n){const{fileName:e,lineNumber:r,columnNumber:a}=n.groups;return`<anonymous>@${e}:${r}:${a}`}}}}}((new Error).stack),{file:o,line:i,keyValues:u}=a??{};await r("plugin:log|log",{level:e,message:n,location:t,file:o,line:i,keyValues:u})}async function u(e){return await o("log://log",(n=>{const{level:r}=n.payload;let{message:a}=n.payload;a=a.replace(/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g,""),e({message:a,level:r})}))}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_ENTER="tauri://drag-enter",e.DRAG_OVER="tauri://drag-over",e.DRAG_DROP="tauri://drag-drop",e.DRAG_LEAVE="tauri://drag-leave"}(a||(a={})),e.LogLevel=void 0,(t=e.LogLevel||(e.LogLevel={}))[t.Trace=1]="Trace",t[t.Debug=2]="Debug",t[t.Info=3]="Info",t[t.Warn=4]="Warn",t[t.Error=5]="Error",e.attachConsole=async function(){return await u((({level:n,message:r})=>{switch(n){case e.LogLevel.Trace:console.log(r);break;case e.LogLevel.Debug:console.debug(r);break;case e.LogLevel.Info:console.info(r);break;case e.LogLevel.Warn:console.warn(r);break;case e.LogLevel.Error:console.error(r);break;default:throw new Error(`unknown log level ${n}`)}}))},e.attachLogger=u,e.debug=async function(n,r){await i(e.LogLevel.Debug,n,r)},e.error=async function(n,r){await i(e.LogLevel.Error,n,r)},e.info=async function(n,r){await i(e.LogLevel.Info,n,r)},e.trace=async function(n,r){await i(e.LogLevel.Trace,n,r)},e.warn=async function(n,r){await i(e.LogLevel.Warn,n,r)},e}({});Object.defineProperty(window.__TAURI__,"log",{value:__TAURI_PLUGIN_LOG__})}
+1 -1
View File
@@ -11,7 +11,7 @@ export interface LogOptions {
keyValues?: Record<string, string | undefined>
}
enum LogLevel {
export enum LogLevel {
/**
* The "trace" level.
*
+49 -8
View File
@@ -2,16 +2,57 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
import UIKit
import Tauri
import SwiftRs
import Tauri
import UIKit
import os.log
#if targetEnvironment(simulator)
var logReady = false
#else
var logReady = true
#endif
var pendingLogs: [(Int, NSString)] = []
var elapsedTime: TimeInterval = 0
var logFlushScheduled = false
@_cdecl("tauri_log")
func log(level: Int, message: NSString) {
switch level {
case 1: Logger.debug(message as String)
case 2: Logger.info(message as String)
case 3: Logger.error(message as String)
default: break
}
if logReady {
os_log(level, message)
} else {
pendingLogs.append((level, message))
scheduleLogFlush()
}
}
// delay logging when the logger isn't immediately available
// in some cases when using the simulator the app would hang when calling os_log too soon
// better be safe here and wait a few seconds than actually freeze the app in dev mode
// in production this isn't a problem
func scheduleLogFlush() {
guard !logFlushScheduled else { return }
logFlushScheduled = true
DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
logReady = true
flushLogs()
}
}
func flushLogs() {
for (level, message) in pendingLogs {
os_log(level, message)
}
pendingLogs.removeAll()
}
func os_log(_ level: Int, _ message: NSString) {
switch level {
case 1: Logger.debug(message as String)
case 2: Logger.info(message as String)
case 3: Logger.error(message as String)
default: break
}
}
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@tauri-apps/plugin-log",
"version": "2.3.1",
"version": "2.6.0",
"description": "Configurable logging for your Tauri app.",
"license": "MIT OR Apache-2.0",
"authors": [
@@ -25,6 +25,6 @@
"LICENSE"
],
"dependencies": {
"@tauri-apps/api": "^2.0.0"
"@tauri-apps/api": "^2.6.0"
}
}
+63 -21
View File
@@ -47,6 +47,7 @@ const DEFAULT_LOG_TARGETS: [Target; 2] = [
Target::new(TargetKind::Stdout),
Target::new(TargetKind::LogDir { file_name: None }),
];
const LOG_DATE_FORMAT: &str = "[year]-[month]-[day]_[hour]-[minute]-[second]";
#[derive(Debug, thiserror::Error)]
pub enum Error {
@@ -115,8 +116,12 @@ impl From<log::Level> for LogLevel {
}
pub enum RotationStrategy {
/// Will keep all the logs, renaming them to include the date.
KeepAll,
/// Will only keep the most recent log up to its maximal size.
KeepOne,
/// Will keep some of the most recent logs, renaming them to include the date.
KeepSome(usize),
}
#[derive(Debug, Clone)]
@@ -577,6 +582,34 @@ pub fn attach_logger(
Ok(())
}
fn rename_file_to_dated(
path: &impl AsRef<Path>,
dir: &impl AsRef<Path>,
file_name: &str,
timezone_strategy: &TimezoneStrategy,
) -> Result<(), Error> {
let to = dir.as_ref().join(format!(
"{}_{}.log",
file_name,
timezone_strategy
.get_now()
.format(&time::format_description::parse(LOG_DATE_FORMAT).unwrap())
.unwrap(),
));
if to.is_file() {
// designated rotated log file name already exists
// highly unlikely but defensively handle anyway by adding .bak to filename
let mut to_bak = to.clone();
to_bak.set_file_name(format!(
"{}.bak",
to_bak.file_name().unwrap().to_string_lossy()
));
fs::rename(&to, to_bak)?;
}
fs::rename(path, to)?;
Ok(())
}
fn get_log_file_path(
dir: &impl AsRef<Path>,
file_name: &str,
@@ -591,27 +624,37 @@ fn get_log_file_path(
if log_size > max_file_size {
match rotation_strategy {
RotationStrategy::KeepAll => {
let to = dir.as_ref().join(format!(
"{}_{}.log",
file_name,
timezone_strategy.get_now().format(&format_description!(
"[year]-[month]-[day]_[hour]-[minute]-[second]"
))?,
));
if to.is_file() {
// designated rotated log file name already exists
// highly unlikely but defensively handle anyway by adding .bak to filename
let mut to_bak = to.clone();
to_bak.set_file_name(format!(
"{}.bak",
to_bak
.file_name()
.map(|f| f.to_string_lossy())
.unwrap_or_default()
));
fs::rename(&to, to_bak)?;
rename_file_to_dated(&path, dir, file_name, timezone_strategy)?;
}
RotationStrategy::KeepSome(how_many) => {
let mut files = fs::read_dir(dir)?
.filter_map(|entry| {
let entry = entry.ok()?;
let path = entry.path();
let old_file_name = path.file_name()?.to_string_lossy().into_owned();
if old_file_name.starts_with(file_name) {
let date = old_file_name
.strip_prefix(file_name)?
.strip_prefix("_")?
.strip_suffix(".log")?;
Some((path, date.to_string()))
} else {
None
}
})
.collect::<Vec<_>>();
// Regular sorting, so the oldest files are first. Lexicographical
// sorting is fine due to the date format.
files.sort_by(|a, b| a.1.cmp(&b.1));
// We want to make space for the file we will be soon renaming, AND
// the file we will be creating. Thus we need to keep how_many - 2 files.
if files.len() > (*how_many - 2) {
files.truncate(files.len() + 2 - *how_many);
for (old_log_path, _) in files {
fs::remove_file(old_log_path)?;
}
}
fs::rename(&path, to)?;
rename_file_to_dated(&path, dir, file_name, timezone_strategy)?;
}
RotationStrategy::KeepOne => {
fs::remove_file(&path)?;
@@ -619,6 +662,5 @@ fn get_log_file_path(
}
}
}
Ok(path)
}
+8
View File
@@ -1,5 +1,13 @@
# Changelog
## \[2.3.0]
- [`f209b2f2`](https://github.com/tauri-apps/plugins-workspace/commit/f209b2f23cb29133c97ad5961fb46ef794dbe063) ([#2804](https://github.com/tauri-apps/plugins-workspace/pull/2804) by [@renovate](https://github.com/tauri-apps/plugins-workspace/../../renovate)) Updated tauri to 2.6
## \[2.2.1]
- [`f634e524`](https://github.com/tauri-apps/plugins-workspace/commit/f634e5248ebe428f8305a59f74c13fc15147fb8e) This is an "empty" release to update the plugins' source files on crates.io and docs.rs. This should fix docs.rs build failures for projects using tauri plugins as dependencies.
## \[2.2.0]
- [`3a79266b`](https://github.com/tauri-apps/plugins-workspace/commit/3a79266b8cf96a55b1ae6339d725567d45a44b1d) ([#2173](https://github.com/tauri-apps/plugins-workspace/pull/2173) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) Bumped all plugins to `v2.2.0`. From now, the versions for the Rust and JavaScript packages of each plugin will be in sync with each other.
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "tauri-plugin-nfc"
version = "2.2.0"
version = "2.3.0"
description = "Read and write NFC tags on Android and iOS."
edition = { workspace = true }
authors = { workspace = true }
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@tauri-apps/plugin-nfc",
"version": "2.2.0",
"version": "2.3.0",
"license": "MIT OR Apache-2.0",
"authors": [
"Tauri Programme within The Commons Conservancy"
@@ -25,6 +25,6 @@
"LICENSE"
],
"dependencies": {
"@tauri-apps/api": "^2.0.0"
"@tauri-apps/api": "^2.6.0"
}
}
@@ -9,8 +9,6 @@ Checking if the NFC functionality is available
and scanning nearby tags is allowed.
Writing to tags needs to be manually enabled.
#### This default permission set includes the following:
- `allow-is-available`
+8
View File
@@ -1,5 +1,13 @@
# Changelog
## \[2.3.0]
- [`f209b2f2`](https://github.com/tauri-apps/plugins-workspace/commit/f209b2f23cb29133c97ad5961fb46ef794dbe063) ([#2804](https://github.com/tauri-apps/plugins-workspace/pull/2804) by [@renovate](https://github.com/tauri-apps/plugins-workspace/../../renovate)) Updated tauri to 2.6
## \[2.2.3]
- [`f634e524`](https://github.com/tauri-apps/plugins-workspace/commit/f634e5248ebe428f8305a59f74c13fc15147fb8e) This is an "empty" release to update the plugins' source files on crates.io and docs.rs. This should fix docs.rs build failures for projects using tauri plugins as dependencies.
## \[2.2.2]
- [`a1b3fa27`](https://github.com/tauri-apps/plugins-workspace/commit/a1b3fa27f11022c9b6622b4fab12d93239eb05de) ([#2515](https://github.com/tauri-apps/plugins-workspace/pull/2515) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) Re-exported the `Geolocation`, `Haptics`, `Notification`, and `Os` structs so that they show up on docs.rs.
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "tauri-plugin-notification"
version = "2.2.2"
version = "2.3.0"
description = "Send desktop and mobile notifications on your Tauri application."
edition = { workspace = true }
authors = { workspace = true }
+1 -1
View File
@@ -1 +1 @@
if("__TAURI__"in window){var __TAURI_PLUGIN_NOTIFICATION__=function(i){"use strict";function n(i,n,t,e){if("function"==typeof n||!n.has(i))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===t?e:"a"===t?e.call(i):e?e.value:n.get(i)}function t(i,n,t,e,o){if("function"==typeof n||!n.has(i))throw new TypeError("Cannot write private member to an object whose class did not declare it");return n.set(i,t),t}var e,o,a;"function"==typeof SuppressedError&&SuppressedError;const r="__TAURI_TO_IPC_KEY__";class c{constructor(){this.__TAURI_CHANNEL_MARKER__=!0,e.set(this,(()=>{})),o.set(this,0),a.set(this,[]),this.id=function(i,n=!1){return window.__TAURI_INTERNALS__.transformCallback(i,n)}((({message:i,id:r})=>{if(r==n(this,o,"f"))for(n(this,e,"f").call(this,i),t(this,o,n(this,o,"f")+1);n(this,o,"f")in n(this,a,"f");){const i=n(this,a,"f")[n(this,o,"f")];n(this,e,"f").call(this,i),delete n(this,a,"f")[n(this,o,"f")],t(this,o,n(this,o,"f")+1)}else n(this,a,"f")[r]=i}))}set onmessage(i){t(this,e,i)}get onmessage(){return n(this,e,"f")}[(e=new WeakMap,o=new WeakMap,a=new WeakMap,r)](){return`__CHANNEL__:${this.id}`}toJSON(){return this[r]()}}class s{constructor(i,n,t){this.plugin=i,this.event=n,this.channelId=t}async unregister(){return u(`plugin:${this.plugin}|remove_listener`,{event:this.event,channelId:this.channelId})}}async function l(i,n,t){const e=new c;return e.onmessage=t,u(`plugin:${i}|registerListener`,{event:n,handler:e}).then((()=>new s(i,n,e.id)))}async function u(i,n={},t){return window.__TAURI_INTERNALS__.invoke(i,n,t)}var f,h,d;i.ScheduleEvery=void 0,(f=i.ScheduleEvery||(i.ScheduleEvery={})).Year="year",f.Month="month",f.TwoWeeks="twoWeeks",f.Week="week",f.Day="day",f.Hour="hour",f.Minute="minute",f.Second="second";return i.Importance=void 0,(h=i.Importance||(i.Importance={}))[h.None=0]="None",h[h.Min=1]="Min",h[h.Low=2]="Low",h[h.Default=3]="Default",h[h.High=4]="High",i.Visibility=void 0,(d=i.Visibility||(i.Visibility={}))[d.Secret=-1]="Secret",d[d.Private=0]="Private",d[d.Public=1]="Public",i.Schedule=class{static at(i,n=!1,t=!1){return{at:{date:i,repeating:n,allowWhileIdle:t},interval:void 0,every:void 0}}static interval(i,n=!1){return{at:void 0,interval:{interval:i,allowWhileIdle:n},every:void 0}}static every(i,n,t=!1){return{at:void 0,interval:void 0,every:{interval:i,count:n,allowWhileIdle:t}}}},i.active=async function(){return await u("plugin:notification|get_active")},i.cancel=async function(i){await u("plugin:notification|cancel",{notifications:i})},i.cancelAll=async function(){await u("plugin:notification|cancel")},i.channels=async function(){return await u("plugin:notification|listChannels")},i.createChannel=async function(i){await u("plugin:notification|create_channel",{...i})},i.isPermissionGranted=async function(){return"default"!==window.Notification.permission?await Promise.resolve("granted"===window.Notification.permission):await u("plugin:notification|is_permission_granted")},i.onAction=async function(i){return await l("notification","actionPerformed",i)},i.onNotificationReceived=async function(i){return await l("notification","notification",i)},i.pending=async function(){return await u("plugin:notification|get_pending")},i.registerActionTypes=async function(i){await u("plugin:notification|register_action_types",{types:i})},i.removeActive=async function(i){await u("plugin:notification|remove_active",{notifications:i})},i.removeAllActive=async function(){await u("plugin:notification|remove_active")},i.removeChannel=async function(i){await u("plugin:notification|delete_channel",{id:i})},i.requestPermission=async function(){return await window.Notification.requestPermission()},i.sendNotification=function(i){"string"==typeof i?new window.Notification(i):new window.Notification(i.title,i)},i}({});Object.defineProperty(window.__TAURI__,"notification",{value:__TAURI_PLUGIN_NOTIFICATION__})}
if("__TAURI__"in window){var __TAURI_PLUGIN_NOTIFICATION__=function(i){"use strict";function n(i,n,t,e){if("function"==typeof n?i!==n||!e:!n.has(i))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===t?e:"a"===t?e.call(i):e?e.value:n.get(i)}function t(i,n,t,e,a){if("function"==typeof n||!n.has(i))throw new TypeError("Cannot write private member to an object whose class did not declare it");return n.set(i,t),t}var e,a,o,r;"function"==typeof SuppressedError&&SuppressedError;const s="__TAURI_TO_IPC_KEY__";class c{constructor(i){e.set(this,void 0),a.set(this,0),o.set(this,[]),r.set(this,void 0),t(this,e,i||(()=>{})),this.id=function(i,n=!1){return window.__TAURI_INTERNALS__.transformCallback(i,n)}((i=>{const s=i.index;if("end"in i)return void(s==n(this,a,"f")?this.cleanupCallback():t(this,r,s));const c=i.message;if(s==n(this,a,"f")){for(n(this,e,"f").call(this,c),t(this,a,n(this,a,"f")+1);n(this,a,"f")in n(this,o,"f");){const i=n(this,o,"f")[n(this,a,"f")];n(this,e,"f").call(this,i),delete n(this,o,"f")[n(this,a,"f")],t(this,a,n(this,a,"f")+1)}n(this,a,"f")===n(this,r,"f")&&this.cleanupCallback()}else n(this,o,"f")[s]=c}))}cleanupCallback(){window.__TAURI_INTERNALS__.unregisterCallback(this.id)}set onmessage(i){t(this,e,i)}get onmessage(){return n(this,e,"f")}[(e=new WeakMap,a=new WeakMap,o=new WeakMap,r=new WeakMap,s)](){return`__CHANNEL__:${this.id}`}toJSON(){return this[s]()}}class l{constructor(i,n,t){this.plugin=i,this.event=n,this.channelId=t}async unregister(){return f(`plugin:${this.plugin}|remove_listener`,{event:this.event,channelId:this.channelId})}}async function u(i,n,t){const e=new c(t);return f(`plugin:${i}|registerListener`,{event:n,handler:e}).then((()=>new l(i,n,e.id)))}async function f(i,n={},t){return window.__TAURI_INTERNALS__.invoke(i,n,t)}var h,d,w;i.ScheduleEvery=void 0,(h=i.ScheduleEvery||(i.ScheduleEvery={})).Year="year",h.Month="month",h.TwoWeeks="twoWeeks",h.Week="week",h.Day="day",h.Hour="hour",h.Minute="minute",h.Second="second";return i.Importance=void 0,(d=i.Importance||(i.Importance={}))[d.None=0]="None",d[d.Min=1]="Min",d[d.Low=2]="Low",d[d.Default=3]="Default",d[d.High=4]="High",i.Visibility=void 0,(w=i.Visibility||(i.Visibility={}))[w.Secret=-1]="Secret",w[w.Private=0]="Private",w[w.Public=1]="Public",i.Schedule=class{static at(i,n=!1,t=!1){return{at:{date:i,repeating:n,allowWhileIdle:t},interval:void 0,every:void 0}}static interval(i,n=!1){return{at:void 0,interval:{interval:i,allowWhileIdle:n},every:void 0}}static every(i,n,t=!1){return{at:void 0,interval:void 0,every:{interval:i,count:n,allowWhileIdle:t}}}},i.active=async function(){return await f("plugin:notification|get_active")},i.cancel=async function(i){await f("plugin:notification|cancel",{notifications:i})},i.cancelAll=async function(){await f("plugin:notification|cancel")},i.channels=async function(){return await f("plugin:notification|listChannels")},i.createChannel=async function(i){await f("plugin:notification|create_channel",{...i})},i.isPermissionGranted=async function(){return"default"!==window.Notification.permission?await Promise.resolve("granted"===window.Notification.permission):await f("plugin:notification|is_permission_granted")},i.onAction=async function(i){return await u("notification","actionPerformed",i)},i.onNotificationReceived=async function(i){return await u("notification","notification",i)},i.pending=async function(){return await f("plugin:notification|get_pending")},i.registerActionTypes=async function(i){await f("plugin:notification|register_action_types",{types:i})},i.removeActive=async function(i){await f("plugin:notification|remove_active",{notifications:i})},i.removeAllActive=async function(){await f("plugin:notification|remove_active")},i.removeChannel=async function(i){await f("plugin:notification|delete_channel",{id:i})},i.requestPermission=async function(){return await window.Notification.requestPermission()},i.sendNotification=function(i){"string"==typeof i?new window.Notification(i):new window.Notification(i.title,i)},i}({});Object.defineProperty(window.__TAURI__,"notification",{value:__TAURI_PLUGIN_NOTIFICATION__})}
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@tauri-apps/plugin-notification",
"version": "2.2.2",
"version": "2.3.0",
"license": "MIT OR Apache-2.0",
"authors": [
"Tauri Programme within The Commons Conservancy"
@@ -24,6 +24,6 @@
"LICENSE"
],
"dependencies": {
"@tauri-apps/api": "^2.0.0"
"@tauri-apps/api": "^2.6.0"
}
}
@@ -7,8 +7,6 @@ notification features are by default exposed.
It allows all notification related features.
#### This default permission set includes the following:
- `allow-is-permission-granted`
+19
View File
@@ -1,5 +1,24 @@
# Changelog
## \[2.4.0]
- [`f209b2f2`](https://github.com/tauri-apps/plugins-workspace/commit/f209b2f23cb29133c97ad5961fb46ef794dbe063) ([#2804](https://github.com/tauri-apps/plugins-workspace/pull/2804) by [@renovate](https://github.com/tauri-apps/plugins-workspace/../../renovate)) Updated tauri to 2.6
## \[2.3.1]
### feat
- [`2aec8ff4`](https://github.com/tauri-apps/plugins-workspace/commit/2aec8ff4c41d178ea9804f7b6eff343c726be015) ([#2803](https://github.com/tauri-apps/plugins-workspace/pull/2803) by [@lucasfernog](https://github.com/tauri-apps/plugins-workspace/../../lucasfernog)) Add `inAppBrowser` option to open URLs in an in-app browser on Android and iOS.
## \[2.3.0]
- [`ce9888a2`](https://github.com/tauri-apps/plugins-workspace/commit/ce9888a2d4c9b449bd2a306f0fa6c76507fd46d3) ([#2762](https://github.com/tauri-apps/plugins-workspace/pull/2762)) Similar to the `fs` plugin the `opener` plugin now supports a `requireLiteralLeadingDot` configuration in `tauri.conf.json`.
## \[2.2.7]
- [`6c9e08dc`](https://github.com/tauri-apps/plugins-workspace/commit/6c9e08dccb3ac99fccfce586fa2b69717ba81b52) ([#2695](https://github.com/tauri-apps/plugins-workspace/pull/2695) by [@ShaunSHamilton](https://github.com/tauri-apps/plugins-workspace/../../ShaunSHamilton)) Adjust `open_url` url type to allow `URL`
- [`dde6f3c3`](https://github.com/tauri-apps/plugins-workspace/commit/dde6f3c31c1b79942abb556be31757dc583f509e) ([#2549](https://github.com/tauri-apps/plugins-workspace/pull/2549) by [@renovate](https://github.com/tauri-apps/plugins-workspace/../../renovate)) Update `windows` crate to 0.61 to align with Tauri 2.5
## \[2.2.6]
- [`1a984659`](https://github.com/tauri-apps/plugins-workspace/commit/1a9846599b6a71faf330845847a30f6bf9735898) ([#2469](https://github.com/tauri-apps/plugins-workspace/pull/2469) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) Update `objc2` crate to 0.6. No user facing changes.
+2 -2
View File
@@ -1,6 +1,6 @@
[package]
name = "tauri-plugin-opener"
version = "2.2.6"
version = "2.4.0"
description = "Open files and URLs using their default application."
edition = { workspace = true }
authors = { workspace = true }
@@ -40,7 +40,7 @@ glob = { workspace = true }
dunce = { workspace = true }
[target."cfg(windows)".dependencies.windows]
version = "0.60"
version = "0.61"
features = [
"Win32_Foundation",
"Win32_UI_Shell_Common",
+1
View File
@@ -35,5 +35,6 @@ android {
dependencies {
implementation("androidx.core:core-ktx:1.9.0")
implementation("com.fasterxml.jackson.core:jackson-databind:2.15.3")
implementation("androidx.browser:browser:1.8.0")
implementation(project(":tauri-android"))
}
@@ -6,22 +6,36 @@ package app.tauri.opener
import android.app.Activity
import android.content.Intent
import android.net.Uri
import androidx.browser.customtabs.CustomTabsIntent
import app.tauri.annotation.Command
import app.tauri.annotation.TauriPlugin
import app.tauri.plugin.Invoke
import app.tauri.plugin.Plugin
import java.io.File
import androidx.core.net.toUri
import app.tauri.annotation.InvokeArg
@InvokeArg
class OpenArgs {
lateinit var url: String
var with: String? = null
}
@TauriPlugin
class OpenerPlugin(private val activity: Activity) : Plugin(activity) {
@Command
fun open(invoke: Invoke) {
try {
val url = invoke.parseArgs(String::class.java)
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
activity.applicationContext?.startActivity(intent)
val args = invoke.parseArgs(OpenArgs::class.java)
if (args.with == "inAppBrowser") {
val builder = CustomTabsIntent.Builder()
val intent = builder.build()
intent.launchUrl(activity, args.url.toUri())
} else {
val intent = Intent(Intent.ACTION_VIEW, args.url.toUri())
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
activity.applicationContext?.startActivity(intent)
}
invoke.resolve()
} catch (ex: Exception) {
invoke.reject(ex.message)
+6 -1
View File
@@ -35,10 +35,15 @@ import { invoke } from '@tauri-apps/api/core'
*
* @param url The URL to open.
* @param openWith The app to open the URL with. If not specified, defaults to the system default application for the specified url type.
* On mobile, `openWith` can be provided as `inAppBrowser` to open the URL in an in-app browser. Otherwise, it will open the URL in the system default browser.
*
* @since 2.0.0
*/
export async function openUrl(url: string, openWith?: string): Promise<void> {
export async function openUrl(
url: string | URL,
// eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents
openWith?: 'inAppBrowser' | string
): Promise<void> {
await invoke('plugin:opener|open_url', {
url,
with: openWith
+19 -5
View File
@@ -3,21 +3,35 @@
// SPDX-License-Identifier: MIT
import Foundation
import SafariServices
import SwiftRs
import Tauri
import UIKit
import WebKit
struct OpenArgs: Decodable {
let url: String
let with: String?
}
class OpenerPlugin: Plugin {
@objc public func open(_ invoke: Invoke) throws {
do {
let urlString = try invoke.parseArgs(String.self)
if let url = URL(string: urlString) {
if #available(iOS 10, *) {
UIApplication.shared.open(url, options: [:])
let args = try invoke.parseArgs(OpenArgs.self)
if let url = URL(string: args.url) {
if args.with == "inAppBrowser" {
DispatchQueue.main.async {
let safariVC = SFSafariViewController(url: url)
self.manager.viewController?.present(safariVC, animated: true)
}
} else {
UIApplication.shared.openURL(url)
if #available(iOS 10, *) {
UIApplication.shared.open(url, options: [:])
} else {
UIApplication.shared.openURL(url)
}
}
}
invoke.resolve()
} catch {

Some files were not shown because too many files have changed in this diff Show More