dioxus_core/suspense/
mod.rs1mod component;
27pub use component::*;
28
29use crate::innerlude::*;
30use std::{
31 cell::{Cell, Ref, RefCell},
32 fmt::Debug,
33 rc::Rc,
34};
35
36#[derive(Clone, PartialEq, Debug)]
38pub struct SuspendedFuture {
39 origin: ScopeId,
40 task: TaskId,
41}
42
43impl SuspendedFuture {
44 pub fn new(task: Task) -> Self {
46 Self {
47 task: task.id,
48 origin: current_scope_id(),
49 }
50 }
51
52 pub fn task(&self) -> Task {
54 Task::from_id(self.task)
55 }
56
57 pub(crate) fn deep_clone(&self) -> Self {
59 Self {
60 task: self.task,
61 origin: self.origin,
62 }
63 }
64}
65
66impl std::fmt::Display for SuspendedFuture {
67 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
68 write!(f, "SuspendedFuture {{ task: {:?} }}", self.task)
69 }
70}
71
72#[derive(Debug, Clone)]
74pub struct SuspenseContext {
75 inner: Rc<SuspenseBoundaryInner>,
76}
77
78impl PartialEq for SuspenseContext {
79 fn eq(&self, other: &Self) -> bool {
80 Rc::ptr_eq(&self.inner, &other.inner)
81 }
82}
83
84impl SuspenseContext {
85 pub(crate) fn new() -> Self {
87 Self {
88 inner: Rc::new(SuspenseBoundaryInner {
89 rt: Runtime::current(),
90 suspended_tasks: RefCell::new(vec![]),
91 id: Cell::new(ScopeId::ROOT),
92 suspended_nodes: Default::default(),
93 frozen: Default::default(),
94 after_suspense_resolved: Default::default(),
95 }),
96 }
97 }
98
99 pub(crate) fn mount(&self, scope: ScopeId) {
101 self.inner.id.set(scope);
102 }
103
104 pub fn suspended_nodes(&self) -> Option<VNode> {
106 self.inner
107 .suspended_nodes
108 .borrow()
109 .as_ref()
110 .map(|node| node.clone())
111 }
112
113 pub(crate) fn set_suspended_nodes(&self, suspended_nodes: VNode) {
115 self.inner
116 .suspended_nodes
117 .borrow_mut()
118 .replace(suspended_nodes);
119 }
120
121 pub(crate) fn take_suspended_nodes(&self) -> Option<VNode> {
123 self.inner.suspended_nodes.borrow_mut().take()
124 }
125
126 pub fn frozen(&self) -> bool {
128 self.inner.frozen.get()
129 }
130
131 pub fn freeze(&self) {
133 self.inner.frozen.set(true);
134 }
135
136 pub fn has_suspended_tasks(&self) -> bool {
138 !self.inner.suspended_tasks.borrow().is_empty()
139 }
140
141 pub fn is_suspended(&self) -> bool {
143 self.inner.suspended_nodes.borrow().is_some()
144 }
145
146 pub(crate) fn add_suspended_task(&self, task: SuspendedFuture) {
148 self.inner.suspended_tasks.borrow_mut().push(task);
149 self.inner.rt.needs_update(self.inner.id.get());
150 }
151
152 pub(crate) fn remove_suspended_task(&self, task: Task) {
154 self.inner
155 .suspended_tasks
156 .borrow_mut()
157 .retain(|t| t.task != task.id);
158 self.inner.rt.needs_update(self.inner.id.get());
159 }
160
161 pub fn suspended_futures(&self) -> Ref<'_, [SuspendedFuture]> {
163 Ref::map(self.inner.suspended_tasks.borrow(), |tasks| {
164 tasks.as_slice()
165 })
166 }
167
168 pub fn after_suspense_resolved(&self, callback: impl FnOnce() + 'static) {
170 let mut closures = self.inner.after_suspense_resolved.borrow_mut();
171 closures.push(Box::new(callback));
172 }
173
174 pub(crate) fn run_resolved_closures(&self, runtime: &Runtime) {
176 runtime.while_not_rendering(|| {
177 self.inner
178 .after_suspense_resolved
179 .borrow_mut()
180 .drain(..)
181 .for_each(|f| f());
182 })
183 }
184}
185
186pub struct SuspenseBoundaryInner {
188 rt: Rc<Runtime>,
189
190 suspended_tasks: RefCell<Vec<SuspendedFuture>>,
191
192 id: Cell<ScopeId>,
193
194 suspended_nodes: RefCell<Option<VNode>>,
196
197 frozen: Cell<bool>,
199
200 after_suspense_resolved: RefCell<Vec<Box<dyn FnOnce()>>>,
202}
203
204impl Debug for SuspenseBoundaryInner {
205 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
206 f.debug_struct("SuspenseBoundaryInner")
207 .field("suspended_tasks", &self.suspended_tasks)
208 .field("id", &self.id)
209 .field("suspended_nodes", &self.suspended_nodes)
210 .field("frozen", &self.frozen)
211 .finish()
212 }
213}