You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

140 lines
4.1 KiB
Rust

use chrono::DateTime;
use futures::executor;
use yew::{function_component, html, AttrValue, Html, Renderer, ServerRenderer};
#[cfg(debug_assertions)]
use console_error_panic_hook::set_once as set_panic_hook;
#[cfg(debug_assertions)]
use yew::prelude::*;
use crate::{
data::{Attachment, Outbox, Person},
error::Ærror,
};
#[derive(yew::Properties, PartialEq)]
struct Props {
pub outbox: Outbox,
pub author: Person,
pub archive_time: DateTime<chrono::Utc>,
}
#[cfg(not(debug_assertions))]
pub fn render_ssr(
outbox: Outbox,
author: Person,
archive_time: DateTime<chrono::Utc>,
) -> Result<String, Ærror> {
let output_template = include_str!("../index.html");
let output_string = executor::block_on(render_async(outbox, author, archive_time));
Ok(output_template.replace(
"<link data-trunk rel=\"rust\" href=\"Cargo.toml\" />",
&output_string,
))
}
#[cfg(debug_assertions)]
pub fn render_client(
outbox: Outbox,
author: Person,
archive_time: DateTime<chrono::Utc>,
) -> Result<(), Ærror> {
set_panic_hook();
Renderer::<Layout>::with_props(Props {
outbox,
author,
archive_time,
})
.render();
Ok(())
}
async fn render_async(
outbox: Outbox,
author: Person,
archive_time: DateTime<chrono::Utc>,
) -> String {
let renderer = ServerRenderer::<Layout>::with_props(move || -> Props {
Props {
outbox,
author,
archive_time,
}
})
.hydratable(false);
renderer.render().await
}
#[function_component]
fn Layout(props: &Props) -> Html {
html! {
<>
<ProfileHeader outbox={props.outbox.clone()} author={props.author.clone()} archive_time={props.archive_time}/>
</>
}
}
#[function_component]
fn ProfileHeader(props: &Props) -> Html {
html! {
<div class="header">
<img class="banner" src="/media/header.png"/>
<img class="avatar" src="/media/avatar.png"/>
<h1 class="name">{props.author.name.as_str()}</h1>
<a href={props.author.url.clone()} class="username" target="_blank">
{props.author.full_username().unwrap().as_str()}
</a>
{ if props.author.attachments.is_some() {
html! {
<div class="profile-grid">
{
props.author.clone().attachments.unwrap().into_iter().filter_map(|attach| match attach {
Attachment::Property {name, value} => Some((name, value)),
_ => None,
}).map(|(name, value)| {
html! {
<div key={name.clone()} class="profile_property">
<span class="property_name">{name}</span>
<span class="property_value">{
if value.find("<a").is_some() {
Html::from_html_unchecked(AttrValue::from(value))
} else {
Html::from(value)
}
}</span>
</div>
}
}).collect::<Html>()
}
</div>
}
} else {
html! {
<></>
}
}}
<div class="bio">
{Html::from_html_unchecked(
AttrValue::from(
props.author.summary.replace("class=\"u-url mention\"", "class=\"mention\" target=\"_blank\"")
)
)}
</div>
<span class="archived_on">
{"Archived "}
{props.archive_time.format("%b %e, %Y")}
</span>
</div>
}
}
#[function_component]
fn Posts(props: &Props) -> Html {
html! {
<p>{"Posts"}</p>
}
}