feat(dialog): Implemented android save dialog. (#1657)

* Implemented android save dialog.

* small cleanup

* lint

---------

Co-authored-by: Lucas Nogueira <lucas@tauri.app>
This commit is contained in:
mikoto2000
2024-08-13 20:41:30 +09:00
committed by GitHub
parent 5081f30daf
commit bc7eecf420
9 changed files with 112 additions and 12 deletions
+14 -2
View File
@@ -71,6 +71,18 @@ pub struct SaveDialogOptions {
can_create_directories: Option<bool>,
}
#[cfg(mobile)]
fn set_default_path<R: Runtime>(
mut dialog_builder: FileDialogBuilder<R>,
default_path: PathBuf,
) -> FileDialogBuilder<R> {
if let Some(file_name) = default_path.file_name() {
dialog_builder = dialog_builder.set_file_name(file_name.to_string_lossy());
}
dialog_builder
}
#[cfg(desktop)]
fn set_default_path<R: Runtime>(
mut dialog_builder: FileDialogBuilder<R>,
default_path: PathBuf,
@@ -193,9 +205,9 @@ pub(crate) async fn save<R: Runtime>(
dialog: State<'_, Dialog<R>>,
options: SaveDialogOptions,
) -> Result<Option<PathBuf>> {
#[cfg(mobile)]
#[cfg(target_os = "ios")]
return Err(crate::Error::FileSaveDialogNotImplemented);
#[cfg(desktop)]
#[cfg(any(desktop, target_os = "android"))]
{
let mut dialog_builder = dialog.file();
#[cfg(any(windows, target_os = "macos"))]
+2 -2
View File
@@ -18,8 +18,8 @@ pub enum Error {
#[cfg(mobile)]
#[error("Folder picker is not implemented on mobile")]
FolderPickerNotImplemented,
#[cfg(mobile)]
#[error("File save dialog is not implemented on mobile")]
#[cfg(target_os = "ios")]
#[error("File save dialog is not implemented on iOS")]
FileSaveDialogNotImplemented,
#[error(transparent)]
Fs(#[from] tauri_plugin_fs::Error),
+6 -3
View File
@@ -17,8 +17,10 @@ use tauri::{
Manager, Runtime,
};
#[cfg(any(desktop, target_os = "ios"))]
use std::fs;
use std::{
fs,
path::{Path, PathBuf},
sync::mpsc::sync_channel,
};
@@ -273,6 +275,7 @@ pub struct FileDialogBuilder<R: Runtime> {
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct FileDialogPayload<'a> {
file_name: &'a Option<String>,
filters: &'a Vec<Filter>,
multiple: bool,
}
@@ -298,6 +301,7 @@ impl<R: Runtime> FileDialogBuilder<R> {
#[cfg(mobile)]
pub(crate) fn payload(&self, multiple: bool) -> FileDialogPayload<'_> {
FileDialogPayload {
file_name: &self.file_name,
filters: &self.filters,
multiple,
}
@@ -471,7 +475,6 @@ impl<R: Runtime> FileDialogBuilder<R> {
/// })
/// })
/// ```
#[cfg(desktop)]
pub fn save_file<F: FnOnce(Option<PathBuf>) + Send + 'static>(self, f: F) {
save_file(self, f)
}
@@ -572,13 +575,13 @@ impl<R: Runtime> FileDialogBuilder<R> {
/// // the file path is `None` if the user closed the dialog
/// }
/// ```
#[cfg(desktop)]
pub fn blocking_save_file(self) -> Option<PathBuf> {
blocking_fn!(self, save_file)
}
}
// taken from deno source code: https://github.com/denoland/deno/blob/ffffa2f7c44bd26aec5ae1957e0534487d099f48/runtime/ops/fs.rs#L913
#[cfg(desktop)]
#[inline]
fn to_msec(maybe_time: std::result::Result<std::time::SystemTime, std::io::Error>) -> Option<u64> {
match maybe_time {
+23
View File
@@ -1,6 +1,7 @@
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
use std::path::PathBuf;
use serde::{de::DeserializeOwned, Deserialize};
use tauri::{
@@ -49,6 +50,11 @@ struct FilePickerResponse {
files: Vec<FileResponse>,
}
#[derive(Debug, Deserialize)]
struct SaveFileResponse {
file: PathBuf,
}
pub fn pick_file<R: Runtime, F: FnOnce(Option<FileResponse>) + Send + 'static>(
dialog: FileDialogBuilder<R>,
f: F,
@@ -83,6 +89,23 @@ pub fn pick_files<R: Runtime, F: FnOnce(Option<Vec<FileResponse>>) + Send + 'sta
});
}
pub fn save_file<R: Runtime, F: FnOnce(Option<PathBuf>) + Send + 'static>(
dialog: FileDialogBuilder<R>,
f: F,
) {
std::thread::spawn(move || {
let res = dialog
.dialog
.0
.run_mobile_plugin::<SaveFileResponse>("saveFileDialog", dialog.payload(false));
if let Ok(response) = res {
f(Some(response.file))
} else {
f(None)
}
});
}
#[derive(Debug, Deserialize)]
struct ShowMessageDialogResponse {
#[allow(dead_code)]