Skip to main content

email_components/
render.rs

1use stylist::{
2    manager::{StyleManager, render_static},
3    yew::ManagerProvider,
4};
5use yew::{BaseComponent, Html, Properties, ServerRenderer, function_component, html};
6
7use crate::email_html::HEAD_PLACEHOLDER;
8
9pub const DOCTYPE: &str = r#"<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">"#;
10
11pub async fn render<C>(props: C::Properties) -> String
12where
13    C: BaseComponent,
14    C::Properties: Send + Clone,
15{
16    let (writer, reader) = render_static();
17
18    let renderer = ServerRenderer::<Wrapped<C>>::with_props(|| {
19        let style_manager = StyleManager::builder()
20            .writer(writer)
21            .build()
22            .expect("failed to create style manager");
23        Props {
24            style_manager,
25            inner_props: props,
26        }
27    });
28    let rendered = renderer.render().await;
29
30    let style_data = reader.read_style_data();
31    let mut styles = String::new();
32    style_data
33        .write_static_markup(&mut styles)
34        .expect("failed to read styles from style manager");
35
36    let rendered_with_styles = rendered.replace(HEAD_PLACEHOLDER, &styles);
37
38    format!("{DOCTYPE}\n{rendered_with_styles}")
39}
40
41#[derive(Properties, PartialEq)]
42struct Props<P>
43where
44    P: PartialEq,
45{
46    pub style_manager: StyleManager,
47    pub inner_props: P,
48}
49
50#[function_component]
51fn Wrapped<C>(props: &Props<C::Properties>) -> Html
52where
53    C: BaseComponent,
54    C::Properties: Clone,
55{
56    let Props {
57        style_manager,
58        inner_props,
59    } = props;
60
61    html! {
62        <ManagerProvider manager={style_manager.clone()}>
63            <C ..inner_props.clone() />
64        </ManagerProvider>
65    }
66}