use std::any::TypeId;
use std::rc::Rc;
use wasm_bindgen::UnwrapThrowExt;
use crate::core::{MessageContext, MessageResult, Mut, View, ViewMarker};
use crate::{DomView, PodMut, ViewCtx};
pub struct Templated<V>(Rc<V>);
impl<V> ViewMarker for Templated<V> {}
impl<State, Action, V> View<State, Action, ViewCtx> for Templated<V>
where
State: 'static,
Action: 'static,
V: DomView<State, Action>,
{
type Element = V::Element;
type ViewState = <Rc<V> as View<State, Action, ViewCtx>>::ViewState;
fn build(&self, ctx: &mut ViewCtx, app_state: &mut State) -> (Self::Element, Self::ViewState) {
let type_id = TypeId::of::<Self>();
let (element, view_state) = if let Some((template_node, view)) = ctx.templates.get(&type_id)
{
let prev = view.clone();
let prev = prev.downcast_ref::<Rc<V>>().unwrap_throw();
let node = template_node.clone_node_with_deep(true).unwrap_throw();
let (mut el, mut state) =
ctx.with_hydration_node(node, |ctx| prev.build(ctx, app_state));
el.apply_changes();
let pod_mut = PodMut::new(&mut el.node, &mut el.props, &mut el.flags, None, false);
self.0.rebuild(prev, &mut state, ctx, pod_mut, app_state);
(el, state)
} else {
let (element, state) = self.0.build(ctx, app_state);
let template: web_sys::Node = element
.node
.as_ref()
.clone_node_with_deep(true)
.unwrap_throw();
ctx.templates
.insert(type_id, (template, Rc::new(self.0.clone())));
(element, state)
};
(element, view_state)
}
fn rebuild(
&self,
prev: &Self,
view_state: &mut Self::ViewState,
ctx: &mut ViewCtx,
element: Mut<'_, Self::Element>,
app_state: &mut State,
) {
self.0.rebuild(&prev.0, view_state, ctx, element, app_state);
}
fn teardown(
&self,
view_state: &mut Self::ViewState,
ctx: &mut ViewCtx,
element: Mut<'_, Self::Element>,
) {
self.0.teardown(view_state, ctx, element);
}
fn message(
&self,
view_state: &mut Self::ViewState,
message: &mut MessageContext,
element: Mut<'_, Self::Element>,
app_state: &mut State,
) -> MessageResult<Action> {
self.0.message(view_state, message, element, app_state)
}
}
pub fn templated<State, Action, E>(view: impl Into<Rc<E>>) -> Templated<E>
where
E: DomView<State, Action>,
{
Templated(view.into())
}