diff --git a/Cargo.lock b/Cargo.lock index 00d91b0..330c3ea 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -12,6 +12,7 @@ dependencies = [ "flate2", "fs_extra", "futures", + "regex", "serde", "serde_json", "tar", @@ -25,6 +26,15 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "aho-corasick" +version = "0.7.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" +dependencies = [ + "memchr", +] + [[package]] name = "android_system_properties" version = "0.1.5" @@ -863,6 +873,23 @@ dependencies = [ "bitflags", ] +[[package]] +name = "regex" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" + [[package]] name = "rustix" version = "0.36.8" diff --git a/Cargo.toml b/Cargo.toml index ac2831b..f59bdf6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,3 +17,4 @@ yew = { version = "0.20.0", features = ["ssr", "csr"] } console_error_panic_hook = { version ="0.1" } wasm-bindgen = {version = "0.2" } fs_extra = "1.3.0" +regex = "1.7.1" diff --git a/resources/style.css b/resources/style.css index d42f17a..3945325 100644 --- a/resources/style.css +++ b/resources/style.css @@ -97,3 +97,21 @@ h1.name { .bio { font-size: 14px; } + +.posts-info { + border-top: solid 1px gray; + border-bottom: solid 1px gray; + margin: var(--body-padding) calc(var(--body-padding) * -1); + margin-bottom: 0; + padding: var(--body-padding); +} + +.posts { + margin: 0 calc(var(--body-padding) * -1); +} + +.post { + border-bottom: solid 1px gray; + padding: var(--body-padding); +} + diff --git a/src/data.rs b/src/data.rs index d619bd9..d947738 100644 --- a/src/data.rs +++ b/src/data.rs @@ -71,28 +71,28 @@ pub enum Item { #[derive(Deserialize, Debug, PartialEq, Clone)] pub struct CommonFields { - id: String, - actor: String, - published: String, - to: Vec, - cc: Vec, + pub id: String, + pub actor: String, + pub published: String, + pub to: Vec, + pub cc: Vec, } #[derive(Deserialize, Debug, PartialEq, Clone)] #[serde(rename_all = "camelCase")] pub struct Post { - summary: Option, - in_reply_to: Option, - url: String, - sensitive: bool, - content: String, + pub summary: Option, + pub in_reply_to: Option, + pub url: String, + pub sensitive: bool, + pub content: String, #[serde(rename = "attachment")] - attachments: Option>, + pub attachments: Option>, - tag: Option>, - replies: Option, - quote_uri: Option, + pub tag: Option>, + pub replies: Option, + pub quote_uri: Option, } #[derive(Deserialize, Debug, PartialEq, Clone)] diff --git a/src/render.rs b/src/render.rs index cd82902..7701def 100644 --- a/src/render.rs +++ b/src/render.rs @@ -1,5 +1,8 @@ +use std::str::FromStr; + use chrono::DateTime; use futures::executor; +use regex::Regex; use yew::{function_component, html, AttrValue, Html, Renderer, ServerRenderer}; #[cfg(debug_assertions)] @@ -8,7 +11,7 @@ use console_error_panic_hook::set_once as set_panic_hook; use yew::prelude::*; use crate::{ - data::{Attachment, Outbox, Person}, + data::{Attachment, CommonFields, Item, Outbox, Person, Post as DataPost}, error::Ærror, }; @@ -19,6 +22,13 @@ struct Props { pub archive_time: DateTime, } +#[derive(yew::Properties, PartialEq)] +struct PostProps { + pub meta: CommonFields, + pub post: DataPost, + pub author: Person, +} + #[cfg(not(debug_assertions))] pub fn render_ssr( outbox: Outbox, @@ -73,6 +83,7 @@ fn Layout(props: &Props) -> Html { html! { <> + } } @@ -133,7 +144,67 @@ fn ProfileHeader(props: &Props) -> Html { #[function_component] fn Posts(props: &Props) -> Html { + let public = &String::from_str("https://www.w3.org/ns/activitystreams#Public").unwrap(); + let author_str = props.author.clone().id; + + let mut ordered_items = props.outbox.clone().ordered_items; + ordered_items.reverse(); + + let posts: Vec<(CommonFields, DataPost)> = ordered_items + .into_iter() + .filter_map(|item| match item { + Item::Post { meta, object } => { + if meta.to.contains(public) + && object.in_reply_to.is_none() + && meta.actor == author_str + { + Some((meta, object)) + } else { + None + } + } + _ => None, + }) + .collect(); + html! { -

{"Posts"}

+ <> +
+ {posts.len()} + {" posts"} +
+
+ {posts.into_iter().map(|(meta, post)| { + html! { + + } + }).collect::()} +
+ + } +} + +#[function_component] +fn Post(props: &PostProps) -> Html { + let post = props.post.clone(); + let meta = props.meta.clone(); + + let post_time_utc = DateTime::parse_from_rfc3339(meta.published.as_str()).unwrap(); + let post_time = DateTime::::from(post_time_utc); + + let mut content = String::from_str(&post.content).unwrap(); + + if post.quote_uri.is_some() { + let regex = Regex::new(".*").unwrap(); + content = String::from_str(®ex.replace(&content.as_str(), "")).unwrap(); + } + + html! { +
+
+ {Html::from_html_unchecked(AttrValue::from(content))} +
+ +
} }