tauri_use/plugins/
dialog.rs

1use std::path::PathBuf;
2use reactive_graph::{
3    effect::Effect, 
4    owner::LocalStorage, 
5    signal::{signal, WriteSignal}, 
6    traits::{Get as _, GetUntracked as _,  Set as _, UpdateUntracked as _}, 
7    wrappers::read::Signal
8};
9use serde::{Deserialize, Serialize};
10
11use crate::{use_invoke_with_args, use_invoke_with_options, UseTauriError, UseTauriWithReturn};
12
13pub fn use_ask_dialog<T>() -> UseDialogReturn<ConfirmDialogOpions, T>
14where 
15    T: Clone + Send + Sync + 'static
16{
17    let (transfer, set_transfer) = signal(None::<T>);
18    let (dialog_options, set_dialog_options) = signal(None::<(ConfirmDialogOpions, T)>);
19
20    let UseTauriWithReturn {
21        data,
22        error,
23        trigger,
24    } = use_invoke_with_args::<ConfirmDialogOpions, bool>("plugin:dialog|ask");
25
26    Effect::new(move || {
27        if let Some((options, _)) = dialog_options.get() {
28            trigger.set(Some(options));
29        }
30    });
31    
32    Effect::new(move || {
33        if let Some(b) = data.get() {
34            if b {
35                set_transfer.set(Some(dialog_options.get_untracked().unwrap().1));
36            }
37            set_dialog_options.update_untracked(|v| *v = None);
38        }
39    });
40
41    Effect::new(move || {
42        if let Some(_) = error.get() {
43            set_dialog_options.update_untracked(|v| *v = None);
44        }
45    });
46
47    UseDialogReturn { 
48        transfer: transfer.into(), 
49        error: error.into(), 
50        set_dialog_options: set_dialog_options,
51    }
52}
53
54pub fn use_confirm_dialog<T>() -> UseDialogReturn<ConfirmDialogOpions, T>
55where 
56    T: Clone + Send + Sync + 'static
57{
58    let (transfer, set_transfer) = signal(None::<T>);
59    let (dialog_options, set_dialog_options) = signal(None::<(ConfirmDialogOpions, T)>);
60
61    let UseTauriWithReturn {
62        data,
63        error,
64        trigger,
65    } = use_invoke_with_args::<ConfirmDialogOpions, bool>("plugin:dialog|confirm");
66
67    Effect::new(move || {
68        if let Some((options, _)) = dialog_options.get() {
69            trigger.set(Some(options));
70        }
71
72        if let Some(b) = data.get() {
73            if b {
74                set_transfer.set(Some(dialog_options.get_untracked().unwrap().1));
75            }
76            set_dialog_options.update_untracked(|v| *v = None);
77        }
78
79        if let Some(_) = error.get() {
80            set_dialog_options.update_untracked(|v| *v = None);
81        }
82    });
83
84    UseDialogReturn { 
85        transfer: transfer.into(), 
86        error: error.into(), 
87        set_dialog_options: set_dialog_options,
88    }
89}
90
91pub fn use_message_dialog<T>() -> UseDialogReturn<MessageDialogOpions, T>
92where 
93    T: Clone + Send + Sync + 'static
94{
95    let (transfer, set_transfer) = signal(None::<T>);
96    let (dialog_options, set_dialog_options) = signal(None::<(MessageDialogOpions, T)>);
97
98    let UseTauriWithReturn {
99        data,
100        error,
101        trigger,
102    } = use_invoke_with_args::<MessageDialogOpions, ()>("plugin:dialog|message");
103
104    Effect::new(move || {
105        if let Some((options, _)) = dialog_options.get() {
106            trigger.set(Some(options));
107        }
108
109        if let Some(()) = data.get() {
110            set_transfer.set(Some(dialog_options.get_untracked().unwrap().1));
111            set_dialog_options.update_untracked(|v| *v = None);
112        }
113
114        if let Some(_) = error.get() {
115            set_dialog_options.update_untracked(|v| *v = None);
116        }
117    });
118
119    UseDialogReturn { 
120        transfer: transfer.into(), 
121        error: error.into(), 
122        set_dialog_options: set_dialog_options,
123    }
124}
125
126pub fn use_open_dialog() -> UseTauriWithReturn<OpenDialogOptions, Option<OpenDialogReturn>> {
127    #[derive(Clone, Serialize)]
128    struct OptionsWrapper {
129        options: OpenDialogOptions,
130    }
131
132    let (args_wrapper, set_args_wrapper) = signal(None::<OpenDialogOptions>);
133
134    let UseTauriWithReturn { 
135        data, 
136        error, 
137        trigger 
138    } = use_invoke_with_args::<OptionsWrapper, Option<OpenDialogReturn>>("plugin:dialog|open");
139
140    Effect::new(move || {
141        if let Some(options) = args_wrapper.get() {
142            trigger.set(Some(OptionsWrapper { options }));
143            set_args_wrapper.update_untracked(|v| *v = None);
144        }
145    });
146
147    UseTauriWithReturn { 
148        data: data.into(), 
149        error: error.into(), 
150        trigger: set_args_wrapper,
151    }
152}
153
154pub fn use_save_dialog() -> UseTauriWithReturn<SaveDialogOptions, Option<PathBuf>> {
155    use_invoke_with_options::<SaveDialogOptions, Option<PathBuf>>("plugin:dialog|save")
156}
157
158pub struct UseDialogReturn<O, T> 
159where 
160    O: serde::Serialize + Clone + 'static,
161    T: Clone + Send + Sync + 'static,
162{
163    pub transfer: Signal<Option<T>>,
164    pub error: Signal<Option<UseTauriError>, LocalStorage>,
165    pub set_dialog_options: WriteSignal<Option<(O, T)>>,
166}
167
168#[derive(Clone, Deserialize)]
169#[serde(untagged)]
170pub enum OpenDialogReturn {
171    Files(Vec<String>),
172    File(String)
173}
174
175#[derive(Clone, Default, Serialize)]
176#[serde(rename = "camelCase")]
177pub struct ConfirmDialogOpions {
178    pub message: String,
179    #[serde(skip_serializing_if = "Option::is_none")]
180    pub title: Option<&'static str>,
181    #[serde(skip_serializing_if = "Option::is_none")]
182    pub kind: Option<MessageDialogKind>,
183    #[serde(skip_serializing_if = "Option::is_none")]
184    pub ok_label: Option<&'static str>,
185    #[serde(skip_serializing_if = "Option::is_none")]
186    pub cancel_label: Option<&'static str>
187}
188
189impl ConfirmDialogOpions {
190    pub fn new(msg: &str) -> Self {
191        Self {
192            message: msg.to_string(),
193            ..Default::default()
194        }
195    }
196}
197
198#[derive(Clone, Default, Serialize)]
199#[serde(rename = "camelCase")]
200pub struct MessageDialogOpions {
201    pub message: String,
202    #[serde(skip_serializing_if = "Option::is_none")]
203    pub title: Option<&'static str>,
204    #[serde(skip_serializing_if = "Option::is_none")]
205    pub kind: Option<MessageDialogKind>,
206    #[serde(skip_serializing_if = "Option::is_none")]
207    pub ok_label: Option<&'static str>,
208}
209
210impl MessageDialogOpions {
211    pub fn new(msg: &str) -> Self {
212        Self {
213            message: msg.to_string(),
214            ..Default::default()
215        }
216    }
217}
218
219#[derive(Clone, Default, Serialize)]
220#[serde(untagged, rename = "camelCase")]
221pub enum MessageDialogKind {
222    #[default]
223    Info,
224    Warning,
225    Error
226}
227
228#[derive(Clone, Default, Serialize)]
229#[serde(rename = "camelCase")]
230pub struct DialogFilter {
231    pub name: String,
232    pub extensions: Vec<String>,
233}
234
235#[derive(Clone, Default, Serialize)]
236#[serde(rename = "camelCase")]
237pub struct OpenDialogOptions {
238    pub title: Option<String>,
239    pub filters: Vec<DialogFilter>,
240    pub multiple: bool,
241    pub directory: bool,
242    pub default_path: Option<PathBuf>,
243    pub recursive: bool,
244    pub can_create_directories: bool
245}
246
247#[derive(Clone, Default, Serialize)]
248#[serde(rename = "camelCase")]
249pub struct SaveDialogOptions {
250    pub title: Option<String>,
251    pub filters: Vec<DialogFilter>,
252    pub default_path: Option<PathBuf>,
253    pub can_create_directories: bool
254}