mod component;
pub use component::*;
use crate::innerlude::*;
use crate::mount::SuspenseBranch;
use std::{
cell::{Cell, Ref, RefCell},
fmt::Debug,
rc::Rc,
};
#[derive(Clone, PartialEq, Debug)]
pub struct SuspendedFuture {
origin: ScopeId,
task: TaskId,
}
impl SuspendedFuture {
pub fn new(task: Task) -> Self {
Self {
task: task.id,
origin: current_scope_id(),
}
}
pub fn task(&self) -> Task {
Task::from_id(self.task)
}
}
impl std::fmt::Display for SuspendedFuture {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "SuspendedFuture {{ task: {:?} }}", self.task)
}
}
#[derive(Debug, Clone)]
pub struct SuspenseContext {
inner: Rc<SuspenseBoundaryInner>,
}
impl PartialEq for SuspenseContext {
fn eq(&self, other: &Self) -> bool {
Rc::ptr_eq(&self.inner, &other.inner)
}
}
impl SuspenseContext {
pub(crate) fn new() -> Self {
Self {
inner: Rc::new(SuspenseBoundaryInner {
rt: Runtime::current(),
suspended_tasks: RefCell::new(vec![]),
id: Cell::new(ScopeId::ROOT),
suspended_branch: Default::default(),
frozen: Default::default(),
after_suspense_resolved: Default::default(),
}),
}
}
pub(crate) fn mount(&self, scope: ScopeId) {
self.inner.id.set(scope);
}
pub fn suspended_nodes(&self) -> Option<VNode> {
self.suspended_branch().map(|branch| branch.root())
}
pub fn with_suspended_mounted_root<R>(
&self,
with_root: impl FnOnce(MountedVNode<'_>) -> R,
) -> Option<R> {
let branch = self.inner.suspended_branch.borrow();
let branch = branch.as_ref()?;
Some(with_root(branch.mounted_root()))
}
pub(crate) fn suspended_branch(&self) -> Option<SuspenseBranch> {
self.inner.suspended_branch.borrow().clone()
}
pub(crate) fn set_suspended_branch(&self, branch: SuspenseBranch) {
self.inner.suspended_branch.borrow_mut().replace(branch);
}
pub(crate) fn take_suspended_branch(&self) -> Option<SuspenseBranch> {
self.inner.suspended_branch.borrow_mut().take()
}
pub fn frozen(&self) -> bool {
self.inner.frozen.get()
}
pub fn freeze(&self) {
self.inner.frozen.set(true);
}
pub fn has_suspended_tasks(&self) -> bool {
!self.inner.suspended_tasks.borrow().is_empty()
}
pub fn is_suspended(&self) -> bool {
self.inner.suspended_branch.borrow().is_some()
}
pub(crate) fn add_suspended_task(&self, task: SuspendedFuture) {
self.inner.suspended_tasks.borrow_mut().push(task);
self.inner.rt.needs_update(self.inner.id.get());
}
pub(crate) fn remove_suspended_task(&self, task: Task) {
self.inner
.suspended_tasks
.borrow_mut()
.retain(|t| t.task != task.id);
if let Some(scope) = self.inner.rt.try_get_state(self.inner.id.get()) {
scope.needs_update_any(scope.id);
}
}
pub fn suspended_futures(&self) -> Ref<'_, [SuspendedFuture]> {
Ref::map(self.inner.suspended_tasks.borrow(), |tasks| {
tasks.as_slice()
})
}
pub fn after_suspense_resolved(&self, callback: impl FnOnce() + 'static) {
let mut closures = self.inner.after_suspense_resolved.borrow_mut();
closures.push(Box::new(callback));
}
pub(crate) fn run_resolved_closures(&self, runtime: &Runtime) {
runtime.while_not_rendering(|| {
self.inner
.after_suspense_resolved
.borrow_mut()
.drain(..)
.for_each(|f| f());
})
}
}
pub(crate) struct SuspenseBoundaryInner {
rt: Rc<Runtime>,
suspended_tasks: RefCell<Vec<SuspendedFuture>>,
id: Cell<ScopeId>,
suspended_branch: RefCell<Option<SuspenseBranch>>,
frozen: Cell<bool>,
after_suspense_resolved: RefCell<Vec<Box<dyn FnOnce()>>>,
}
impl Debug for SuspenseBoundaryInner {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("SuspenseBoundaryInner")
.field("suspended_tasks", &self.suspended_tasks)
.field("id", &self.id)
.field(
"has_suspended_branch",
&self.suspended_branch.borrow().is_some(),
)
.field("frozen", &self.frozen)
.finish()
}
}