tauri_use/plugins/
dialog.rs

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