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.
136 lines
4.0 KiB
Rust
136 lines
4.0 KiB
Rust
use chrono::DateTime;
|
|
use futures::executor;
|
|
use yew::{function_component, html, Html, Renderer, ServerRenderer, AttrValue};
|
|
|
|
#[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_string = executor::block_on(render_async(outbox, author, archive_time));
|
|
|
|
Ok(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>
|
|
}
|
|
}
|