use base64::Engine;
use dioxus_lib::prelude::dioxus_core::DynamicNode;
use dioxus_lib::prelude::{has_context, ErrorContext, ScopeId, SuspenseContext, VNode, VirtualDom};
use super::SerializeContext;
impl super::HTMLData {
pub(crate) fn extract_from_suspense_boundary(vdom: &VirtualDom, scope: ScopeId) -> Self {
let mut data = Self::default();
data.serialize_errors(vdom, scope);
data.take_from_scope(vdom, scope);
data
}
fn serialize_errors(&mut self, vdom: &VirtualDom, scope: ScopeId) {
let error = vdom.in_runtime(|| {
scope
.consume_context::<ErrorContext>()
.and_then(|error_context| error_context.errors().first().cloned())
});
self.push(&error, std::panic::Location::caller());
}
fn take_from_scope(&mut self, vdom: &VirtualDom, scope: ScopeId) {
vdom.in_runtime(|| {
scope.in_runtime(|| {
let context: Option<SerializeContext> = has_context();
if let Some(context) = context {
self.extend(&context.data.borrow());
}
});
});
if let Some(scope) = vdom.get_scope(scope) {
if let Some(suspense_boundary) =
SuspenseContext::downcast_suspense_boundary_from_scope(&vdom.runtime(), scope.id())
{
if let Some(node) = suspense_boundary.suspended_nodes() {
self.take_from_vnode(vdom, &node);
}
}
if let Some(node) = scope.try_root_node() {
self.take_from_vnode(vdom, node);
}
}
}
fn take_from_vnode(&mut self, vdom: &VirtualDom, vnode: &VNode) {
for (dynamic_node_index, dyn_node) in vnode.dynamic_nodes.iter().enumerate() {
match dyn_node {
DynamicNode::Component(comp) => {
if let Some(scope) = comp.mounted_scope(dynamic_node_index, vnode, vdom) {
self.take_from_scope(vdom, scope.id());
}
}
DynamicNode::Fragment(nodes) => {
for node in nodes {
self.take_from_vnode(vdom, node);
}
}
_ => {}
}
}
}
#[cfg(feature = "server")]
pub(crate) fn serialized(&self) -> SerializedHydrationData {
let mut serialized = Vec::new();
ciborium::into_writer(&self.data, &mut serialized).unwrap();
let data = base64::engine::general_purpose::STANDARD.encode(serialized);
let format_js_list_of_strings = |list: &[Option<String>]| {
let body = list
.iter()
.map(|s| match s {
Some(s) => format!(r#""{s}""#),
None => r#""unknown""#.to_string(),
})
.collect::<Vec<_>>()
.join(",");
format!("[{}]", body)
};
SerializedHydrationData {
data,
#[cfg(debug_assertions)]
debug_types: format_js_list_of_strings(&self.debug_types),
#[cfg(debug_assertions)]
debug_locations: format_js_list_of_strings(&self.debug_locations),
}
}
}
#[cfg(feature = "server")]
pub(crate) struct SerializedHydrationData {
pub data: String,
#[cfg(debug_assertions)]
pub debug_types: String,
#[cfg(debug_assertions)]
pub debug_locations: String,
}