1use crate::{use_callback, use_signal};
2use dioxus_core::{use_hook, Callback, CapturedError, Result, Task};
3use dioxus_signals::{ReadSignal, ReadableBoxExt, ReadableExt, Signal, WritableExt};
4use futures_channel::oneshot::Receiver;
5use futures_util::{future::Shared, FutureExt};
6use std::{marker::PhantomData, pin::Pin, prelude::rust_2024::Future, task::Poll};
7
8pub fn use_action<E, C, M>(mut user_fn: C) -> Action<C::Input, C::Output>
9where
10 E: Into<CapturedError> + 'static,
11 C: ActionCallback<M, E>,
12 M: 'static,
13 C::Input: 'static,
14 C::Output: 'static,
15 C: 'static,
16{
17 let mut value = use_signal(|| None as Option<C::Output>);
18 let mut error = use_signal(|| None as Option<CapturedError>);
19 let mut task = use_signal(|| None as Option<Task>);
20 let mut state = use_signal(|| ActionState::Unset);
21 let callback = use_callback(move |input: C::Input| {
22 if let Some(task) = task.take() {
24 task.cancel();
25 }
26
27 let (tx, rx) = futures_channel::oneshot::channel();
28 let rx = rx.shared();
29
30 let result = user_fn.call(input);
32 let new_task = dioxus_core::spawn(async move {
33 state.set(ActionState::Pending);
35
36 let result = result.await;
38 match result {
39 Ok(res) => {
40 error.set(None);
41 value.set(Some(res));
42 state.set(ActionState::Ready);
43 }
44 Err(err) => {
45 error.set(Some(err.into()));
46 value.set(None);
47 state.set(ActionState::Errored);
48 }
49 }
50
51 tx.send(()).ok();
52 });
53
54 task.set(Some(new_task));
55
56 rx
57 });
58
59 let reader = use_hook(|| value.boxed().map(|v| v.as_ref().unwrap()).boxed());
62
63 Action {
64 value,
65 error,
66 task,
67 callback,
68 reader,
69 _phantom: PhantomData,
70 state,
71 }
72}
73
74pub struct Action<I, T: 'static> {
75 reader: ReadSignal<T>,
76 error: Signal<Option<CapturedError>>,
77 value: Signal<Option<T>>,
78 task: Signal<Option<Task>>,
79 callback: Callback<I, Shared<Receiver<()>>>,
80 state: Signal<ActionState>,
81 _phantom: PhantomData<*const I>,
82}
83
84impl<I: 'static, O: 'static> Action<I, O> {
85 pub fn value(&self) -> Option<Result<ReadSignal<O>, CapturedError>> {
86 if !matches!(
87 *self.state.read(),
88 ActionState::Ready | ActionState::Errored
89 ) {
90 return None;
91 }
92
93 if let Some(err) = self.error.cloned() {
94 return Some(Err(err));
95 }
96
97 if self.value.read().is_none() {
98 return None;
99 }
100
101 Some(Ok(self.reader))
102 }
103
104 pub fn pending(&self) -> bool {
105 *self.state.read() == ActionState::Pending
106 }
107
108 pub fn reset(&mut self) {
110 self.state.set(ActionState::Reset);
111 if let Some(t) = self.task.take() {
112 t.cancel()
113 }
114 }
115
116 pub fn cancel(&mut self) {
117 if let Some(t) = self.task.take() {
118 t.cancel()
119 }
120 self.state.set(ActionState::Reset);
121 }
122}
123
124impl<I, T> std::fmt::Debug for Action<I, T>
125where
126 T: std::fmt::Debug + 'static,
127{
128 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
129 if f.alternate() {
130 f.debug_struct("Action")
131 .field("state", &self.state.read())
132 .field("value", &self.value.read())
133 .field("error", &self.error.read())
134 .finish()
135 } else {
136 std::fmt::Debug::fmt(&self.value.read().as_ref(), f)
137 }
138 }
139}
140pub struct Dispatching<I> {
141 _phantom: PhantomData<*const I>,
142 receiver: Shared<Receiver<()>>,
143}
144
145impl<T> Dispatching<T> {
146 pub(crate) fn new(receiver: Shared<Receiver<()>>) -> Self {
147 Self {
148 _phantom: PhantomData,
149 receiver,
150 }
151 }
152}
153
154impl<T> std::future::Future for Dispatching<T> {
155 type Output = ();
156
157 fn poll(mut self: Pin<&mut Self>, _cx: &mut std::task::Context<'_>) -> Poll<Self::Output> {
158 match self.receiver.poll_unpin(_cx) {
159 Poll::Ready(_) => Poll::Ready(()),
160 Poll::Pending => Poll::Pending,
161 }
162 }
163}
164
165impl<I, T> Copy for Action<I, T> {}
166impl<I, T> Clone for Action<I, T> {
167 fn clone(&self) -> Self {
168 *self
169 }
170}
171
172#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
176enum ActionState {
177 Unset,
178 Pending,
179 Ready,
180 Errored,
181 Reset,
182}
183
184pub trait ActionCallback<M, E> {
185 type Input;
186 type Output;
187 fn call(
188 &mut self,
189 input: Self::Input,
190 ) -> impl Future<Output = Result<Self::Output, E>> + 'static;
191}
192
193impl<F, O, G, E> ActionCallback<(O,), E> for F
194where
195 F: FnMut() -> G,
196 G: Future<Output = Result<O, E>> + 'static,
197{
198 type Input = ();
199 type Output = O;
200 fn call(
201 &mut self,
202 _input: Self::Input,
203 ) -> impl Future<Output = Result<Self::Output, E>> + 'static {
204 (self)()
205 }
206}
207
208impl<F, O, A, G, E> ActionCallback<(A, O), E> for F
209where
210 F: FnMut(A) -> G,
211 G: Future<Output = Result<O, E>> + 'static,
212{
213 type Input = (A,);
214 type Output = O;
215 fn call(
216 &mut self,
217 input: Self::Input,
218 ) -> impl Future<Output = Result<Self::Output, E>> + 'static {
219 let (a,) = input;
220 (self)(a)
221 }
222}
223
224impl<O, A, B, F, G, E> ActionCallback<(A, B, O), E> for F
225where
226 F: FnMut(A, B) -> G,
227 G: Future<Output = Result<O, E>> + 'static,
228{
229 type Input = (A, B);
230 type Output = O;
231 fn call(
232 &mut self,
233 input: Self::Input,
234 ) -> impl Future<Output = Result<Self::Output, E>> + 'static {
235 let (a, b) = input;
236 (self)(a, b)
237 }
238}
239
240impl<O, A, B, C, F, G, E> ActionCallback<(A, B, C, O), E> for F
241where
242 F: FnMut(A, B, C) -> G,
243 G: Future<Output = Result<O, E>> + 'static,
244{
245 type Input = (A, B, C);
246 type Output = O;
247 fn call(
248 &mut self,
249 input: Self::Input,
250 ) -> impl Future<Output = Result<Self::Output, E>> + 'static {
251 let (a, b, c) = input;
252 (self)(a, b, c)
253 }
254}
255
256impl<O> Action<(), O> {
257 pub fn call(&mut self) -> Dispatching<()> {
258 Dispatching::new((self.callback).call(()))
259 }
260}
261
262impl<A: 'static, O> Action<(A,), O> {
263 pub fn call(&mut self, _a: A) -> Dispatching<()> {
264 Dispatching::new((self.callback).call((_a,)))
265 }
266}
267
268impl<A: 'static, B: 'static, O> Action<(A, B), O> {
269 pub fn call(&mut self, _a: A, _b: B) -> Dispatching<()> {
270 Dispatching::new((self.callback).call((_a, _b)))
271 }
272}
273
274impl<A: 'static, B: 'static, C: 'static, O> Action<(A, B, C), O> {
275 pub fn call(&mut self, _a: A, _b: B, _c: C) -> Dispatching<()> {
276 Dispatching::new((self.callback).call((_a, _b, _c)))
277 }
278}