Skip to content

Commit 1367c60

Browse files
committed
feat: dialog add stop_accessing_path command
1 parent 715e28d commit 1367c60

File tree

10 files changed

+152
-44
lines changed

10 files changed

+152
-44
lines changed

plugins/dialog/build.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// SPDX-License-Identifier: Apache-2.0
33
// SPDX-License-Identifier: MIT
44

5-
const COMMANDS: &[&str] = &["open", "save", "message", "ask", "confirm"];
5+
const COMMANDS: &[&str] = &["open", "save", "stop_accessing_path", "message", "ask", "confirm"];
66

77
fn main() {
88
let result = tauri_plugin::Builder::new(COMMANDS)

plugins/dialog/guest-js/index.ts

Lines changed: 45 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,31 @@
44

55
import { invoke } from '@tauri-apps/api/core'
66

7+
class Path {
8+
public path: string
9+
constructor(path: string) {
10+
this.path = path
11+
}
12+
13+
destroy() {
14+
return invoke('plugin:dialog|stop-accessing-path', { path: this.path })
15+
}
16+
17+
toPath() {
18+
return this.path
19+
}
20+
21+
toString() {
22+
return this.toPath()
23+
}
24+
25+
toJSON() {
26+
return {
27+
path: this.path
28+
}
29+
}
30+
}
31+
732
/**
833
* Extension filters for the file dialog.
934
*
@@ -100,13 +125,7 @@ interface ConfirmDialogOptions {
100125
cancelLabel?: string
101126
}
102127

103-
type OpenDialogReturn<T extends OpenDialogOptions> = T['directory'] extends true
104-
? T['multiple'] extends true
105-
? string[] | null
106-
: string | null
107-
: T['multiple'] extends true
108-
? string[] | null
109-
: string | null
128+
type OpenDialogReturn<T extends OpenDialogOptions> = T['multiple'] extends true ? Path[] | null : Path | null
110129

111130
/**
112131
* Open a file/directory selection dialog.
@@ -171,7 +190,17 @@ async function open<T extends OpenDialogOptions>(
171190
Object.freeze(options)
172191
}
173192

174-
return await invoke('plugin:dialog|open', { options })
193+
const path = await invoke<string[] | string | null>('plugin:dialog|open', { options })
194+
195+
if (Array.isArray(path)) {
196+
return path.map((p) => new Path(p))
197+
}
198+
199+
if (!path) {
200+
return null
201+
}
202+
203+
return new Path(path)
175204
}
176205

177206
/**
@@ -202,12 +231,18 @@ async function open<T extends OpenDialogOptions>(
202231
*
203232
* @since 2.0.0
204233
*/
205-
async function save(options: SaveDialogOptions = {}): Promise<string | null> {
234+
async function save(options: SaveDialogOptions = {}): Promise<Path | null> {
206235
if (typeof options === 'object') {
207236
Object.freeze(options)
208237
}
209238

210-
return await invoke('plugin:dialog|save', { options })
239+
const path = await invoke<string | null>('plugin:dialog|save', { options })
240+
241+
if (!path) {
242+
return null
243+
}
244+
245+
return new Path(path)
211246
}
212247

213248
/**

plugins/dialog/ios/Sources/DialogPlugin.swift

Lines changed: 12 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ struct SaveFileDialogOptions: Decodable {
3838
var defaultPath: String?
3939
}
4040

41+
struct StopAccessingPathOptions: Decodable {
42+
var path: URL
43+
}
44+
4145
class DialogPlugin: Plugin {
4246

4347
var filePickerController: FilePickerController!
@@ -74,14 +78,8 @@ class DialogPlugin: Plugin {
7478
onFilePickerResult = { (event: FilePickerEvent) -> Void in
7579
switch event {
7680
case .selected(let urls):
77-
do {
78-
let temporaryUrls = try urls.map { try self.saveTemporaryFile($0) }
79-
invoke.resolve(["files": temporaryUrls])
80-
} catch {
81-
let message = "Failed to create a temporary copy of the file: \(error)"
82-
Logger.error("\(message)")
83-
invoke.reject(message)
84-
}
81+
urls.forEach { $0.startAccessingSecurityScopedResource() }
82+
invoke.resolve(["files": urls])
8583
case .cancelled:
8684
invoke.resolve(["files": nil])
8785
case .error(let error):
@@ -179,6 +177,12 @@ class DialogPlugin: Plugin {
179177
}
180178
}
181179

180+
@objc public func stopAccessingPath(_ invoke: Invoke) throws {
181+
let args = try invoke.parseArgs(StopAccessingPathOptions.self)
182+
args.path.stopAccessingSecurityScopedResource()
183+
invoke.resolve()
184+
}
185+
182186
private func presentViewController(_ viewControllerToPresent: UIViewController) {
183187
self.manager.viewController?.present(viewControllerToPresent, animated: true, completion: nil)
184188
}
@@ -203,27 +207,6 @@ class DialogPlugin: Plugin {
203207
self.onFilePickerResult?(event)
204208
}
205209

206-
private func saveTemporaryFile(_ sourceUrl: URL) throws -> URL {
207-
var directory = URL(fileURLWithPath: NSTemporaryDirectory())
208-
if let cachesDirectory = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask)
209-
.first
210-
{
211-
directory = cachesDirectory
212-
}
213-
let targetUrl = directory.appendingPathComponent(sourceUrl.lastPathComponent)
214-
do {
215-
try deleteFile(targetUrl)
216-
}
217-
try FileManager.default.copyItem(at: sourceUrl, to: targetUrl)
218-
return targetUrl
219-
}
220-
221-
private func deleteFile(_ url: URL) throws {
222-
if FileManager.default.fileExists(atPath: url.path) {
223-
try FileManager.default.removeItem(atPath: url.path)
224-
}
225-
}
226-
227210
@objc public func showMessageDialog(_ invoke: Invoke) throws {
228211
let manager = self.manager
229212
let args = try invoke.parseArgs(MessageDialogOptions.self)
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Automatically generated - DO NOT EDIT!
2+
3+
"$schema" = "../../schemas/schema.json"
4+
5+
[[permission]]
6+
identifier = "allow-stop-accessing-path"
7+
description = "Enables the stop_accessing_path command without any pre-configured scope."
8+
commands.allow = ["stop_accessing_path"]
9+
10+
[[permission]]
11+
identifier = "deny-stop-accessing-path"
12+
description = "Denies the stop_accessing_path command without any pre-configured scope."
13+
commands.deny = ["stop_accessing_path"]

plugins/dialog/permissions/autogenerated/reference.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ All dialog types are enabled.
1414
- `allow-message`
1515
- `allow-save`
1616
- `allow-open`
17+
- `allow-stop-accessing-path`
1718

1819
## Permission Table
1920

@@ -151,6 +152,32 @@ Enables the save command without any pre-configured scope.
151152

152153
Denies the save command without any pre-configured scope.
153154

155+
</td>
156+
</tr>
157+
158+
<tr>
159+
<td>
160+
161+
`dialog:allow-stop-accessing-path`
162+
163+
</td>
164+
<td>
165+
166+
Enables the stop_accessing_path command without any pre-configured scope.
167+
168+
</td>
169+
</tr>
170+
171+
<tr>
172+
<td>
173+
174+
`dialog:deny-stop-accessing-path`
175+
176+
</td>
177+
<td>
178+
179+
Denies the stop_accessing_path command without any pre-configured scope.
180+
154181
</td>
155182
</tr>
156183
</table>

plugins/dialog/permissions/default.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,5 @@ permissions = [
1717
"allow-message",
1818
"allow-save",
1919
"allow-open",
20+
"allow-stop-accessing-path"
2021
]

plugins/dialog/permissions/schemas/schema.json

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -355,10 +355,22 @@
355355
"markdownDescription": "Denies the save command without any pre-configured scope."
356356
},
357357
{
358-
"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`",
358+
"description": "Enables the stop_accessing_path command without any pre-configured scope.",
359+
"type": "string",
360+
"const": "allow-stop-accessing-path",
361+
"markdownDescription": "Enables the stop_accessing_path command without any pre-configured scope."
362+
},
363+
{
364+
"description": "Denies the stop_accessing_path command without any pre-configured scope.",
365+
"type": "string",
366+
"const": "deny-stop-accessing-path",
367+
"markdownDescription": "Denies the stop_accessing_path command without any pre-configured scope."
368+
},
369+
{
370+
"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`",
359371
"type": "string",
360372
"const": "default",
361-
"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`"
373+
"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`"
362374
}
363375
]
364376
}

plugins/dialog/src/commands.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use tauri::{command, Manager, Runtime, State, Window};
99
use tauri_plugin_fs::FsExt;
1010

1111
use crate::{
12+
StopAccessingPath,
1213
Dialog, FileDialogBuilder, FilePath, MessageDialogButtons, MessageDialogKind, Result, CANCEL,
1314
NO, OK, YES,
1415
};
@@ -241,6 +242,11 @@ pub(crate) async fn save<R: Runtime>(
241242
Ok(path.map(|p| p.simplified()))
242243
}
243244

245+
#[command]
246+
pub fn stop_accessing_path(_p: StopAccessingPath) -> bool {
247+
true
248+
}
249+
244250
fn message_dialog<R: Runtime>(
245251
#[allow(unused_variables)] window: Window<R>,
246252
dialog: State<'_, Dialog<R>>,

plugins/dialog/src/lib.rs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
html_favicon_url = "https://github.com/tauri-apps/tauri/raw/dev/app-icon.png"
1010
)]
1111

12-
use serde::Serialize;
12+
use serde::{Deserialize, Serialize};
1313
use tauri::{
1414
plugin::{Builder, TauriPlugin},
1515
Manager, Runtime,
@@ -180,6 +180,7 @@ pub fn init<R: Runtime>() -> TauriPlugin<R> {
180180
.invoke_handler(tauri::generate_handler![
181181
commands::open,
182182
commands::save,
183+
commands::stop_accessing_path,
183184
commands::message,
184185
commands::ask,
185186
commands::confirm
@@ -337,6 +338,11 @@ pub(crate) struct FileDialogPayload<'a> {
337338
multiple: bool,
338339
}
339340

341+
#[derive(Debug, Serialize, Deserialize)]
342+
pub struct StopAccessingPath {
343+
path: FilePath,
344+
}
345+
340346
// raw window handle :(
341347
unsafe impl<R: Runtime> Send for FileDialogBuilder<R> {}
342348

@@ -566,6 +572,10 @@ impl<R: Runtime> FileDialogBuilder<R> {
566572
pub fn save_file<F: FnOnce(Option<FilePath>) + Send + 'static>(self, f: F) {
567573
save_file(self, f)
568574
}
575+
576+
pub fn stop_accessing_path(self, p: StopAccessingPath) -> bool {
577+
stop_accessing_path(self, p)
578+
}
569579
}
570580

571581
/// Blocking APIs.
@@ -678,4 +688,8 @@ impl<R: Runtime> FileDialogBuilder<R> {
678688
pub fn blocking_save_file(self) -> Option<FilePath> {
679689
blocking_fn!(self, save_file)
680690
}
691+
692+
pub fn blocking_stop_accessing_path(self, p: StopAccessingPath) -> bool {
693+
self.stop_accessing_path(p)
694+
}
681695
}

plugins/dialog/src/mobile.rs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use tauri::{
88
AppHandle, Runtime,
99
};
1010

11-
use crate::{FileDialogBuilder, FilePath, MessageDialogBuilder};
11+
use crate::{FileDialogBuilder, FilePath, MessageDialogBuilder, StopAccessingPath};
1212

1313
#[cfg(target_os = "android")]
1414
const PLUGIN_IDENTIFIER: &str = "app.tauri.dialog";
@@ -105,6 +105,23 @@ pub fn save_file<R: Runtime, F: FnOnce(Option<FilePath>) + Send + 'static>(
105105
});
106106
}
107107

108+
#[allow(unused_variables)]
109+
pub fn stop_accessing_path<R: Runtime>(dialog: FileDialogBuilder<R>, p: StopAccessingPath) -> bool {
110+
#[cfg(target_os = "ios")]
111+
{
112+
let res = dialog
113+
.dialog
114+
.0
115+
.run_mobile_plugin::<()>("stopAccessingPath", p);
116+
117+
if let Err(_) = res {
118+
return false
119+
}
120+
}
121+
122+
true
123+
}
124+
108125
#[derive(Debug, Deserialize)]
109126
struct ShowMessageDialogResponse {
110127
#[allow(dead_code)]

0 commit comments

Comments
 (0)