#![allow(missing_docs)]
use crate::{use_callback, use_hook_did_run, use_signal};
use dioxus_core::{use_hook, Callback, Subscribers, Task};
use dioxus_signals::*;
use std::future::Future;
use std::ops::Deref;
#[doc = include_str!("../docs/rules_of_hooks.md")]
#[doc = include_str!("../docs/moving_state_around.md")]
#[doc(alias = "use_async")]
pub fn use_future<F>(mut future: impl FnMut() -> F + 'static) -> UseFuture
where
F: Future + 'static,
{
let mut state = use_signal(|| UseFutureState::Pending);
let callback = use_callback(move |_| {
let fut = future();
dioxus_core::spawn(async move {
state.set(UseFutureState::Pending);
fut.await;
state.set(UseFutureState::Ready);
})
});
let task = use_hook(|| CopyValue::new(callback(())));
use_hook_did_run(move |did_run| match did_run {
true => task.peek().resume(),
false => task.peek().pause(),
});
UseFuture {
task,
state,
callback,
}
}
#[derive(Clone, Copy, PartialEq)]
pub struct UseFuture {
task: CopyValue<Task>,
state: Signal<UseFutureState>,
callback: Callback<(), Task>,
}
#[derive(Clone, Copy, PartialEq, Hash, Eq, Debug)]
pub enum UseFutureState {
Pending,
Stopped,
Paused,
Ready,
}
impl UseFuture {
pub fn restart(&mut self) {
self.task.write().cancel();
let new_task = self.callback.call(());
self.task.set(new_task);
}
pub fn cancel(&mut self) {
self.state.set(UseFutureState::Stopped);
self.task.write().cancel();
}
pub fn pause(&mut self) {
self.state.set(UseFutureState::Paused);
self.task.write().pause();
}
pub fn resume(&mut self) {
if self.finished() {
return;
}
self.state.set(UseFutureState::Pending);
self.task.write().resume();
}
pub fn task(&self) -> Task {
self.task.cloned()
}
pub fn finished(&self) -> bool {
matches!(
*self.state.peek(),
UseFutureState::Ready | UseFutureState::Stopped
)
}
pub fn state(&self) -> ReadSignal<UseFutureState> {
self.state.into()
}
}
impl From<UseFuture> for ReadSignal<UseFutureState> {
fn from(val: UseFuture) -> Self {
val.state.into()
}
}
impl Readable for UseFuture {
type Target = UseFutureState;
type Storage = UnsyncStorage;
#[track_caller]
fn try_read_unchecked(
&self,
) -> Result<ReadableRef<'static, Self>, generational_box::BorrowError> {
self.state.try_read_unchecked()
}
#[track_caller]
fn try_peek_unchecked(
&self,
) -> Result<ReadableRef<'static, Self>, generational_box::BorrowError> {
self.state.try_peek_unchecked()
}
fn subscribers(&self) -> Subscribers {
self.state.subscribers()
}
}
impl Deref for UseFuture {
type Target = dyn Fn() -> UseFutureState;
fn deref(&self) -> &Self::Target {
unsafe { ReadableExt::deref_impl(self) }
}
}