mod component;
pub use component::*;
use crate::innerlude::*;
use std::{
cell::{Cell, Ref, RefCell},
fmt::Debug,
rc::Rc,
};
#[derive(Clone, PartialEq, Debug)]
pub struct SuspendedFuture {
origin: ScopeId,
task: Task,
pub(crate) placeholder: VNode,
}
impl SuspendedFuture {
pub fn new(task: Task) -> Self {
Self {
task,
origin: current_scope_id().unwrap_or_else(|e| panic!("{}", e)),
placeholder: VNode::placeholder(),
}
}
pub fn suspense_placeholder(&self) -> Option<VNode> {
if self.placeholder == VNode::placeholder() {
None
} else {
Some(self.placeholder.clone())
}
}
pub fn with_placeholder(mut self, placeholder: VNode) -> Self {
self.placeholder = placeholder;
self
}
pub fn task(&self) -> Task {
self.task
}
pub(crate) fn deep_clone(&self) -> Self {
Self {
task: self.task,
placeholder: self.placeholder.deep_clone(),
origin: self.origin,
}
}
}
#[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 {
suspended_tasks: RefCell::new(vec![]),
id: Cell::new(ScopeId::ROOT),
suspended_nodes: Default::default(),
frozen: Default::default(),
}),
}
}
pub(crate) fn mount(&self, scope: ScopeId) {
self.inner.id.set(scope);
}
pub fn suspended_nodes(&self) -> Option<VNode> {
self.inner
.suspended_nodes
.borrow()
.as_ref()
.map(|node| node.clone())
}
pub(crate) fn set_suspended_nodes(&self, suspended_nodes: VNode) {
self.inner
.suspended_nodes
.borrow_mut()
.replace(suspended_nodes);
}
pub(crate) fn take_suspended_nodes(&self) -> Option<VNode> {
self.inner.suspended_nodes.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_nodes.borrow().is_some()
}
pub(crate) fn add_suspended_task(&self, task: SuspendedFuture) {
self.inner.suspended_tasks.borrow_mut().push(task);
self.inner.id.get().needs_update();
}
pub(crate) fn remove_suspended_task(&self, task: Task) {
self.inner
.suspended_tasks
.borrow_mut()
.retain(|t| t.task != task);
self.inner.id.get().needs_update();
}
pub fn suspended_futures(&self) -> Ref<[SuspendedFuture]> {
Ref::map(self.inner.suspended_tasks.borrow(), |tasks| {
tasks.as_slice()
})
}
pub fn suspense_placeholder(&self) -> Option<Element> {
self.inner
.suspended_tasks
.borrow()
.iter()
.find_map(|task| task.suspense_placeholder())
.map(std::result::Result::Ok)
}
}
#[derive(Debug)]
pub struct SuspenseBoundaryInner {
suspended_tasks: RefCell<Vec<SuspendedFuture>>,
id: Cell<ScopeId>,
suspended_nodes: RefCell<Option<VNode>>,
frozen: Cell<bool>,
}
pub trait SuspenseExtension<T>: private::Sealed {
fn with_loading_placeholder(
self,
display_placeholder: impl FnOnce() -> Element,
) -> std::result::Result<T, RenderError>;
}
impl<T> SuspenseExtension<T> for std::result::Result<T, RenderError> {
fn with_loading_placeholder(
self,
display_placeholder: impl FnOnce() -> Element,
) -> std::result::Result<T, RenderError> {
if let Err(RenderError::Suspended(suspense)) = self {
Err(RenderError::Suspended(suspense.with_placeholder(
display_placeholder().unwrap_or_default(),
)))
} else {
self
}
}
}
pub(crate) mod private {
use super::*;
pub trait Sealed {}
impl<T> Sealed for std::result::Result<T, RenderError> {}
}