1use std::{future::Future, rc::Rc};
2
3use sycamore::{
4 futures::spawn_local_scoped,
5 reactive::{create_ref, create_signal, use_context, ReadSignal, Scope, Signal},
6};
7
8use crate::{client::QueryOptions, QueryClient, QueryData, Status};
9
10pub struct Mutation<'a, T, E, Args> {
31 pub data: &'a ReadSignal<QueryData<Rc<T>, Rc<E>>>,
33 pub status: &'a ReadSignal<Status>,
35 pub mutate: &'a dyn Fn(Args),
38}
39
40impl QueryClient {
41 pub(crate) fn run_mutation<'a, T, E, Mutate, R, Args, Success>(
42 &self,
43 cx: Scope<'a>,
44 data: &'a Signal<QueryData<Rc<T>, Rc<E>>>,
45 status: &'a Signal<Status>,
46 mutator: &'a Mutate,
47 args: Args,
48 on_success: &'a Success,
49 ) where
50 Mutate: Fn(Args) -> R,
51 R: Future<Output = Result<T, E>>,
52 Success: Fn(Rc<QueryClient>, Rc<T>),
53 Args: 'a,
54 {
55 status.set(Status::Fetching);
56 spawn_local_scoped(cx, async move {
57 let res = mutator(args).await;
58 data.set(res.map_or_else(
59 |err| QueryData::Err(Rc::new(err)),
60 |data| QueryData::Ok(Rc::new(data)),
61 ));
62 if let QueryData::Ok(ok) = data.get().as_ref() {
63 let client = use_context::<Rc<QueryClient>>(cx);
64 on_success(client.clone(), ok.clone());
65 }
66 status.set(Status::Success);
67 });
68 }
69}
70
71pub fn use_mutation<'a, Args, T, E, F, R, Success>(
101 cx: Scope<'a>,
102 mutator: F,
103 on_success: Success,
104) -> Mutation<'a, T, E, Args>
105where
106 F: Fn(Args) -> R + 'a,
107 R: Future<Output = Result<T, E>>,
108 Success: Fn(Rc<QueryClient>, Rc<T>) + 'a,
109{
110 use_mutation_with_options(cx, mutator, on_success, QueryOptions::default())
111}
112
113pub fn use_mutation_with_options<'a, Args, T, E, F, R, Success>(
116 cx: Scope<'a>,
117 mutator: F,
118 on_success: Success,
119 _options: QueryOptions,
120) -> Mutation<'a, T, E, Args>
121where
122 F: Fn(Args) -> R + 'a,
123 R: Future<Output = Result<T, E>>,
124 Success: Fn(Rc<QueryClient>, Rc<T>) + 'a,
125{
126 let client = use_context::<Rc<QueryClient>>(cx).clone();
127 let data: &Signal<QueryData<Rc<T>, Rc<E>>> = create_signal(cx, QueryData::Loading);
128 let status = create_signal(cx, Status::Fetching);
129 let mutator = create_ref(cx, mutator);
130 let on_success = create_ref(cx, on_success);
131
132 let mutate = create_ref(cx, move |args: Args| {
133 client.run_mutation(cx, data, status, mutator, args, on_success)
134 });
135
136 Mutation {
137 data,
138 mutate,
139 status,
140 }
141}