use std::fmt;
use std::marker::PhantomData;
use std::rc::Rc;
use yew::platform::pinned::oneshot;
use yew::prelude::*;
use super::traits::{Mutation, MutationResult};
use crate::states::future_notion::{use_future_notion_runner, FutureNotion};
use crate::states::input_selector::use_input_selector_value;
use crate::states::slice::use_slice_dispatch;
use super::mutation_states::{
HandleId, MutationId, MutationSelector, MutationSlice, MutationSliceAction, MutationSliceValue,
RunMutation, RunMutationInput,
};
#[derive(Debug, PartialEq)]
pub enum MutationState<T>
where
T: Mutation + 'static,
{
Idle,
Loading,
Completed {
result: MutationResult<T>,
},
Refreshing {
last_result: MutationResult<T>,
},
}
impl<T> Clone for MutationState<T>
where
T: Mutation + 'static,
{
fn clone(&self) -> Self {
match self {
Self::Idle => Self::Idle,
Self::Loading => Self::Loading,
Self::Completed { result } => Self::Completed {
result: result.clone(),
},
Self::Refreshing { last_result } => Self::Refreshing {
last_result: last_result.clone(),
},
}
}
}
impl<T> PartialEq<&MutationState<T>> for MutationState<T>
where
T: Mutation + 'static,
{
fn eq(&self, other: &&MutationState<T>) -> bool {
self == *other
}
}
impl<T> PartialEq<MutationState<T>> for &'_ MutationState<T>
where
T: Mutation + 'static,
{
fn eq(&self, other: &MutationState<T>) -> bool {
*self == other
}
}
pub struct UseMutationHandle<T>
where
T: Mutation + 'static,
{
id: HandleId,
state: Rc<MutationState<T>>,
run_mutation: Rc<dyn Fn(<RunMutation<T> as FutureNotion>::Input)>,
_marker: PhantomData<T>,
}
impl<T> UseMutationHandle<T>
where
T: Mutation + 'static,
{
pub fn state(&self) -> &MutationState<T> {
self.state.as_ref()
}
pub fn result(&self) -> Option<&MutationResult<T>> {
match self.state() {
MutationState::Idle | MutationState::Loading => None,
MutationState::Completed { result }
| MutationState::Refreshing {
last_result: result,
} => Some(result),
}
}
pub async fn run(&self, input: impl Into<Rc<T::Input>>) -> MutationResult<T> {
let id = MutationId::default();
let input = input.into();
let (sender, receiver) = oneshot::channel();
(self.run_mutation)(RunMutationInput {
handle_id: self.id,
mutation_id: id,
input,
sender: Some(sender).into(),
});
receiver.await.unwrap()
}
}
impl<T> fmt::Debug for UseMutationHandle<T>
where
T: Mutation + fmt::Debug + 'static,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("UseMutationHandle")
.field("state", &self.state)
.finish()
}
}
impl<T> Clone for UseMutationHandle<T>
where
T: Mutation + 'static,
{
fn clone(&self) -> Self {
Self {
id: self.id,
state: self.state.clone(),
run_mutation: self.run_mutation.clone(),
_marker: PhantomData,
}
}
}
#[hook]
pub fn use_mutation<T>() -> UseMutationHandle<T>
where
T: Mutation + 'static,
{
let id = *use_memo((), |_| HandleId::default());
let dispatch_state = use_slice_dispatch::<MutationSlice<T>>();
let run_mutation = use_future_notion_runner::<RunMutation<T>>();
let state = use_input_selector_value::<MutationSelector<T>>(id.into());
{
use_effect_with(id, |id| {
let id = *id;
dispatch_state(MutationSliceAction::Create(id));
move || {
dispatch_state(MutationSliceAction::Destroy(id));
}
});
}
let state = use_memo(state, |state| match state.value.as_ref() {
Some(MutationSliceValue::Idle) | None => MutationState::Idle,
Some(MutationSliceValue::Loading { .. }) => MutationState::Loading,
Some(MutationSliceValue::Completed { result, .. }) => MutationState::Completed {
result: result.clone(),
},
Some(MutationSliceValue::Outdated { result, .. }) => MutationState::Refreshing {
last_result: result.clone(),
},
});
UseMutationHandle {
id,
state,
run_mutation,
_marker: PhantomData,
}
}