feat: dialog add stop_accessing_path command

pull/2548/head
nashaofu 2 days ago committed by nashaofu
parent 715e28d922
commit 1367c60fc9

@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
const COMMANDS: &[&str] = &["open", "save", "message", "ask", "confirm"];
const COMMANDS: &[&str] = &["open", "save", "stop_accessing_path", "message", "ask", "confirm"];
fn main() {
let result = tauri_plugin::Builder::new(COMMANDS)

@ -4,6 +4,31 @@
import { invoke } from '@tauri-apps/api/core'
class Path {
public path: string
constructor(path: string) {
this.path = path
}
destroy() {
return invoke('plugin:dialog|stop-accessing-path', { path: this.path })
}
toPath() {
return this.path
}
toString() {
return this.toPath()
}
toJSON() {
return {
path: this.path
}
}
}
/**
* Extension filters for the file dialog.
*
@ -100,13 +125,7 @@ interface ConfirmDialogOptions {
cancelLabel?: string
}
type OpenDialogReturn<T extends OpenDialogOptions> = T['directory'] extends true
? T['multiple'] extends true
? string[] | null
: string | null
: T['multiple'] extends true
? string[] | null
: string | null
type OpenDialogReturn<T extends OpenDialogOptions> = T['multiple'] extends true ? Path[] | null : Path | null
/**
* Open a file/directory selection dialog.
@ -171,7 +190,17 @@ async function open<T extends OpenDialogOptions>(
Object.freeze(options)
}
return await invoke('plugin:dialog|open', { options })
const path = await invoke<string[] | string | null>('plugin:dialog|open', { options })
if (Array.isArray(path)) {
return path.map((p) => new Path(p))
}
if (!path) {
return null
}
return new Path(path)
}
/**
@ -202,12 +231,18 @@ async function open<T extends OpenDialogOptions>(
*
* @since 2.0.0
*/
async function save(options: SaveDialogOptions = {}): Promise<string | null> {
async function save(options: SaveDialogOptions = {}): Promise<Path | null> {
if (typeof options === 'object') {
Object.freeze(options)
}
return await invoke('plugin:dialog|save', { options })
const path = await invoke<string | null>('plugin:dialog|save', { options })
if (!path) {
return null
}
return new Path(path)
}
/**

@ -38,6 +38,10 @@ struct SaveFileDialogOptions: Decodable {
var defaultPath: String?
}
struct StopAccessingPathOptions: Decodable {
var path: URL
}
class DialogPlugin: Plugin {
var filePickerController: FilePickerController!
@ -74,14 +78,8 @@ class DialogPlugin: Plugin {
onFilePickerResult = { (event: FilePickerEvent) -> Void in
switch event {
case .selected(let urls):
do {
let temporaryUrls = try urls.map { try self.saveTemporaryFile($0) }
invoke.resolve(["files": temporaryUrls])
} catch {
let message = "Failed to create a temporary copy of the file: \(error)"
Logger.error("\(message)")
invoke.reject(message)
}
urls.forEach { $0.startAccessingSecurityScopedResource() }
invoke.resolve(["files": urls])
case .cancelled:
invoke.resolve(["files": nil])
case .error(let error):
@ -179,6 +177,12 @@ class DialogPlugin: Plugin {
}
}
@objc public func stopAccessingPath(_ invoke: Invoke) throws {
let args = try invoke.parseArgs(StopAccessingPathOptions.self)
args.path.stopAccessingSecurityScopedResource()
invoke.resolve()
}
private func presentViewController(_ viewControllerToPresent: UIViewController) {
self.manager.viewController?.present(viewControllerToPresent, animated: true, completion: nil)
}
@ -203,27 +207,6 @@ class DialogPlugin: Plugin {
self.onFilePickerResult?(event)
}
private func saveTemporaryFile(_ sourceUrl: URL) throws -> URL {
var directory = URL(fileURLWithPath: NSTemporaryDirectory())
if let cachesDirectory = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask)
.first
{
directory = cachesDirectory
}
let targetUrl = directory.appendingPathComponent(sourceUrl.lastPathComponent)
do {
try deleteFile(targetUrl)
}
try FileManager.default.copyItem(at: sourceUrl, to: targetUrl)
return targetUrl
}
private func deleteFile(_ url: URL) throws {
if FileManager.default.fileExists(atPath: url.path) {
try FileManager.default.removeItem(atPath: url.path)
}
}
@objc public func showMessageDialog(_ invoke: Invoke) throws {
let manager = self.manager
let args = try invoke.parseArgs(MessageDialogOptions.self)

@ -0,0 +1,13 @@
# Automatically generated - DO NOT EDIT!
"$schema" = "../../schemas/schema.json"
[[permission]]
identifier = "allow-stop-accessing-path"
description = "Enables the stop_accessing_path command without any pre-configured scope."
commands.allow = ["stop_accessing_path"]
[[permission]]
identifier = "deny-stop-accessing-path"
description = "Denies the stop_accessing_path command without any pre-configured scope."
commands.deny = ["stop_accessing_path"]

@ -14,6 +14,7 @@ All dialog types are enabled.
- `allow-message`
- `allow-save`
- `allow-open`
- `allow-stop-accessing-path`
## Permission Table
@ -151,6 +152,32 @@ Enables the save command without any pre-configured scope.
Denies the save command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`dialog:allow-stop-accessing-path`
</td>
<td>
Enables the stop_accessing_path command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`dialog:deny-stop-accessing-path`
</td>
<td>
Denies the stop_accessing_path command without any pre-configured scope.
</td>
</tr>
</table>

@ -17,4 +17,5 @@ permissions = [
"allow-message",
"allow-save",
"allow-open",
"allow-stop-accessing-path"
]

@ -355,10 +355,22 @@
"markdownDescription": "Denies the save command without any pre-configured scope."
},
{
"description": "This permission set configures the types of dialogs\navailable from the dialog plugin.\n\n#### Granted Permissions\n\nAll dialog types are enabled.\n\n\n\n#### This default permission set includes:\n\n- `allow-ask`\n- `allow-confirm`\n- `allow-message`\n- `allow-save`\n- `allow-open`",
"description": "Enables the stop_accessing_path command without any pre-configured scope.",
"type": "string",
"const": "allow-stop-accessing-path",
"markdownDescription": "Enables the stop_accessing_path command without any pre-configured scope."
},
{
"description": "Denies the stop_accessing_path command without any pre-configured scope.",
"type": "string",
"const": "deny-stop-accessing-path",
"markdownDescription": "Denies the stop_accessing_path command without any pre-configured scope."
},
{
"description": "This permission set configures the types of dialogs\navailable from the dialog plugin.\n\n#### Granted Permissions\n\nAll dialog types are enabled.\n\n\n\n#### This default permission set includes:\n\n- `allow-ask`\n- `allow-confirm`\n- `allow-message`\n- `allow-save`\n- `allow-open`\n- `allow-stop-accessing-path`",
"type": "string",
"const": "default",
"markdownDescription": "This permission set configures the types of dialogs\navailable from the dialog plugin.\n\n#### Granted Permissions\n\nAll dialog types are enabled.\n\n\n\n#### This default permission set includes:\n\n- `allow-ask`\n- `allow-confirm`\n- `allow-message`\n- `allow-save`\n- `allow-open`"
"markdownDescription": "This permission set configures the types of dialogs\navailable from the dialog plugin.\n\n#### Granted Permissions\n\nAll dialog types are enabled.\n\n\n\n#### This default permission set includes:\n\n- `allow-ask`\n- `allow-confirm`\n- `allow-message`\n- `allow-save`\n- `allow-open`\n- `allow-stop-accessing-path`"
}
]
}

@ -9,6 +9,7 @@ use tauri::{command, Manager, Runtime, State, Window};
use tauri_plugin_fs::FsExt;
use crate::{
StopAccessingPath,
Dialog, FileDialogBuilder, FilePath, MessageDialogButtons, MessageDialogKind, Result, CANCEL,
NO, OK, YES,
};
@ -241,6 +242,11 @@ pub(crate) async fn save<R: Runtime>(
Ok(path.map(|p| p.simplified()))
}
#[command]
pub fn stop_accessing_path(_p: StopAccessingPath) -> bool {
true
}
fn message_dialog<R: Runtime>(
#[allow(unused_variables)] window: Window<R>,
dialog: State<'_, Dialog<R>>,

@ -9,7 +9,7 @@
html_favicon_url = "https://github.com/tauri-apps/tauri/raw/dev/app-icon.png"
)]
use serde::Serialize;
use serde::{Deserialize, Serialize};
use tauri::{
plugin::{Builder, TauriPlugin},
Manager, Runtime,
@ -180,6 +180,7 @@ pub fn init<R: Runtime>() -> TauriPlugin<R> {
.invoke_handler(tauri::generate_handler![
commands::open,
commands::save,
commands::stop_accessing_path,
commands::message,
commands::ask,
commands::confirm
@ -337,6 +338,11 @@ pub(crate) struct FileDialogPayload<'a> {
multiple: bool,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct StopAccessingPath {
path: FilePath,
}
// raw window handle :(
unsafe impl<R: Runtime> Send for FileDialogBuilder<R> {}
@ -566,6 +572,10 @@ impl<R: Runtime> FileDialogBuilder<R> {
pub fn save_file<F: FnOnce(Option<FilePath>) + Send + 'static>(self, f: F) {
save_file(self, f)
}
pub fn stop_accessing_path(self, p: StopAccessingPath) -> bool {
stop_accessing_path(self, p)
}
}
/// Blocking APIs.
@ -678,4 +688,8 @@ impl<R: Runtime> FileDialogBuilder<R> {
pub fn blocking_save_file(self) -> Option<FilePath> {
blocking_fn!(self, save_file)
}
pub fn blocking_stop_accessing_path(self, p: StopAccessingPath) -> bool {
self.stop_accessing_path(p)
}
}

@ -8,7 +8,7 @@ use tauri::{
AppHandle, Runtime,
};
use crate::{FileDialogBuilder, FilePath, MessageDialogBuilder};
use crate::{FileDialogBuilder, FilePath, MessageDialogBuilder, StopAccessingPath};
#[cfg(target_os = "android")]
const PLUGIN_IDENTIFIER: &str = "app.tauri.dialog";
@ -105,6 +105,23 @@ pub fn save_file<R: Runtime, F: FnOnce(Option<FilePath>) + Send + 'static>(
});
}
#[allow(unused_variables)]
pub fn stop_accessing_path<R: Runtime>(dialog: FileDialogBuilder<R>, p: StopAccessingPath) -> bool {
#[cfg(target_os = "ios")]
{
let res = dialog
.dialog
.0
.run_mobile_plugin::<()>("stopAccessingPath", p);
if let Err(_) = res {
return false
}
}
true
}
#[derive(Debug, Deserialize)]
struct ShowMessageDialogResponse {
#[allow(dead_code)]

Loading…
Cancel
Save