diff --git a/.gitignore b/.gitignore index 92bc629..0f17700 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ test-archive.tar.gz out/ dist/ +temp/ # Added by cargo /target diff --git a/Trunk.toml b/Trunk.toml index 0a3e74f..70695d1 100644 --- a/Trunk.toml +++ b/Trunk.toml @@ -3,7 +3,24 @@ release = false dist = "dist" public_url = "/" +[watch] +ignore = ["temp"] + [serve] address = "0.0.0.0" port = 3000 +[[hooks]] +stage = "pre_build" +command = "sh" +command_arguments = ["-c", "mkdir -p ./temp && tar -xzf ./test-archive.tar.gz -C ./temp"] + +[[hooks]] +stage = "pre_build" +command = "cp" +command_arguments = ["-r", "./temp/", "./dist/.stage/media" ] + +[[hooks]] +stage = "pre_build" +command = "cp" +command_arguments = ["-r", "./resources/", "./dist/.stage/resources" ] diff --git a/index.html b/index.html index faeecbc..c3f6d2e 100644 --- a/index.html +++ b/index.html @@ -3,7 +3,8 @@ - Trunk | Yew | YBC + Mastodon Export + @@ -11,3 +12,6 @@ + + + diff --git a/resources/fonts/roboto.css b/resources/fonts/roboto.css new file mode 100644 index 0000000..a1383ae --- /dev/null +++ b/resources/fonts/roboto.css @@ -0,0 +1,126 @@ +/* cyrillic-ext */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url(./roboto/KFOmCnqEu92Fr1Mu72xKOzY.woff2) format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url(./roboto/KFOmCnqEu92Fr1Mu5mxKOzY.woff2) format('woff2'); + unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* greek-ext */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url(./roboto/KFOmCnqEu92Fr1Mu7mxKOzY.woff2) format('woff2'); + unicode-range: U+1F00-1FFF; +} +/* greek */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url(./roboto/KFOmCnqEu92Fr1Mu4WxKOzY.woff2) format('woff2'); + unicode-range: U+0370-03FF; +} +/* vietnamese */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url(./roboto/KFOmCnqEu92Fr1Mu7WxKOzY.woff2) format('woff2'); + unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB; +} +/* latin-ext */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url(./roboto/KFOmCnqEu92Fr1Mu7GxKOzY.woff2) format('woff2'); + unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url(./roboto/KFOmCnqEu92Fr1Mu4mxK.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +/* cyrillic-ext */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 500; + font-display: swap; + src: url(./roboto/KFOlCnqEu92Fr1MmEU9fCRc4EsA.woff2) format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 500; + font-display: swap; + src: url(./roboto/KFOlCnqEu92Fr1MmEU9fABc4EsA.woff2) format('woff2'); + unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* greek-ext */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 500; + font-display: swap; + src: url(./roboto/KFOlCnqEu92Fr1MmEU9fCBc4EsA.woff2) format('woff2'); + unicode-range: U+1F00-1FFF; +} +/* greek */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 500; + font-display: swap; + src: url(./roboto/KFOlCnqEu92Fr1MmEU9fBxc4EsA.woff2) format('woff2'); + unicode-range: U+0370-03FF; +} +/* vietnamese */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 500; + font-display: swap; + src: url(./roboto/KFOlCnqEu92Fr1MmEU9fCxc4EsA.woff2) format('woff2'); + unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB; +} +/* latin-ext */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 500; + font-display: swap; + src: url(./roboto/KFOlCnqEu92Fr1MmEU9fChc4EsA.woff2) format('woff2'); + unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 500; + font-display: swap; + src: url(./roboto/KFOlCnqEu92Fr1MmEU9fBBc4.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} diff --git a/resources/fonts/roboto/KFOlCnqEu92Fr1MmEU9fABc4EsA.woff2 b/resources/fonts/roboto/KFOlCnqEu92Fr1MmEU9fABc4EsA.woff2 new file mode 100644 index 0000000..cb5834f Binary files /dev/null and b/resources/fonts/roboto/KFOlCnqEu92Fr1MmEU9fABc4EsA.woff2 differ diff --git a/resources/fonts/roboto/KFOlCnqEu92Fr1MmEU9fBBc4.woff2 b/resources/fonts/roboto/KFOlCnqEu92Fr1MmEU9fBBc4.woff2 new file mode 100644 index 0000000..29342a8 Binary files /dev/null and b/resources/fonts/roboto/KFOlCnqEu92Fr1MmEU9fBBc4.woff2 differ diff --git a/resources/fonts/roboto/KFOlCnqEu92Fr1MmEU9fBxc4EsA.woff2 b/resources/fonts/roboto/KFOlCnqEu92Fr1MmEU9fBxc4EsA.woff2 new file mode 100644 index 0000000..0933dfe Binary files /dev/null and b/resources/fonts/roboto/KFOlCnqEu92Fr1MmEU9fBxc4EsA.woff2 differ diff --git a/resources/fonts/roboto/KFOlCnqEu92Fr1MmEU9fCBc4EsA.woff2 b/resources/fonts/roboto/KFOlCnqEu92Fr1MmEU9fCBc4EsA.woff2 new file mode 100644 index 0000000..064e94b Binary files /dev/null and b/resources/fonts/roboto/KFOlCnqEu92Fr1MmEU9fCBc4EsA.woff2 differ diff --git a/resources/fonts/roboto/KFOlCnqEu92Fr1MmEU9fCRc4EsA.woff2 b/resources/fonts/roboto/KFOlCnqEu92Fr1MmEU9fCRc4EsA.woff2 new file mode 100644 index 0000000..8571683 Binary files /dev/null and b/resources/fonts/roboto/KFOlCnqEu92Fr1MmEU9fCRc4EsA.woff2 differ diff --git a/resources/fonts/roboto/KFOlCnqEu92Fr1MmEU9fChc4EsA.woff2 b/resources/fonts/roboto/KFOlCnqEu92Fr1MmEU9fChc4EsA.woff2 new file mode 100644 index 0000000..68f094c Binary files /dev/null and b/resources/fonts/roboto/KFOlCnqEu92Fr1MmEU9fChc4EsA.woff2 differ diff --git a/resources/fonts/roboto/KFOlCnqEu92Fr1MmEU9fCxc4EsA.woff2 b/resources/fonts/roboto/KFOlCnqEu92Fr1MmEU9fCxc4EsA.woff2 new file mode 100644 index 0000000..6b0b4af Binary files /dev/null and b/resources/fonts/roboto/KFOlCnqEu92Fr1MmEU9fCxc4EsA.woff2 differ diff --git a/resources/fonts/roboto/KFOmCnqEu92Fr1Mu4WxKOzY.woff2 b/resources/fonts/roboto/KFOmCnqEu92Fr1Mu4WxKOzY.woff2 new file mode 100644 index 0000000..fc71d94 Binary files /dev/null and b/resources/fonts/roboto/KFOmCnqEu92Fr1Mu4WxKOzY.woff2 differ diff --git a/resources/fonts/roboto/KFOmCnqEu92Fr1Mu4mxK.woff2 b/resources/fonts/roboto/KFOmCnqEu92Fr1Mu4mxK.woff2 new file mode 100644 index 0000000..020729e Binary files /dev/null and b/resources/fonts/roboto/KFOmCnqEu92Fr1Mu4mxK.woff2 differ diff --git a/resources/fonts/roboto/KFOmCnqEu92Fr1Mu5mxKOzY.woff2 b/resources/fonts/roboto/KFOmCnqEu92Fr1Mu5mxKOzY.woff2 new file mode 100644 index 0000000..47da362 Binary files /dev/null and b/resources/fonts/roboto/KFOmCnqEu92Fr1Mu5mxKOzY.woff2 differ diff --git a/resources/fonts/roboto/KFOmCnqEu92Fr1Mu72xKOzY.woff2 b/resources/fonts/roboto/KFOmCnqEu92Fr1Mu72xKOzY.woff2 new file mode 100644 index 0000000..22ddee9 Binary files /dev/null and b/resources/fonts/roboto/KFOmCnqEu92Fr1Mu72xKOzY.woff2 differ diff --git a/resources/fonts/roboto/KFOmCnqEu92Fr1Mu7GxKOzY.woff2 b/resources/fonts/roboto/KFOmCnqEu92Fr1Mu7GxKOzY.woff2 new file mode 100644 index 0000000..8a8de61 Binary files /dev/null and b/resources/fonts/roboto/KFOmCnqEu92Fr1Mu7GxKOzY.woff2 differ diff --git a/resources/fonts/roboto/KFOmCnqEu92Fr1Mu7WxKOzY.woff2 b/resources/fonts/roboto/KFOmCnqEu92Fr1Mu7WxKOzY.woff2 new file mode 100644 index 0000000..6284d2e Binary files /dev/null and b/resources/fonts/roboto/KFOmCnqEu92Fr1Mu7WxKOzY.woff2 differ diff --git a/resources/fonts/roboto/KFOmCnqEu92Fr1Mu7mxKOzY.woff2 b/resources/fonts/roboto/KFOmCnqEu92Fr1Mu7mxKOzY.woff2 new file mode 100644 index 0000000..72ce0e9 Binary files /dev/null and b/resources/fonts/roboto/KFOmCnqEu92Fr1Mu7mxKOzY.woff2 differ diff --git a/resources/style.css b/resources/style.css new file mode 100644 index 0000000..d42f17a --- /dev/null +++ b/resources/style.css @@ -0,0 +1,99 @@ +@import url('./fonts/roboto.css'); + +:root { + --page-background: lightgray; + --body-background: white; + --body-width: 600px; + --body-padding: 15px; + --profile-size: 120px; + --cover-size: 250px; +} + +html, body { + margin: 0; + padding: 0; + background: var(--page-background); + font-family: 'Roboto'; +} + +body { + max-width: var(--body-width); + min-height: 100vh; + margin: 0 auto; + background: var(--body-background); + box-sizing: border-box; + padding: var(--body-padding); +} + +img.banner { + width: calc(100% + 2 * var(--body-padding)); + object-fit: cover; + object-position: center; + max-height: var(--cover-size); + margin: calc(-1 * var(--body-padding)); +} + +img.avatar { + width: var(--profile-size); + height: var(--profile-size); + border: solid 2px white; + border-radius: var(--body-padding); + margin-top: calc(var(--profile-size) / -2 + var(--body-padding)); + display: inline-block; +} + +h1.name { + font-size: 16px; + line-height: 24px; + font-weight: 500; + margin-bottom: 0; +} + +.username, .archived_on { + font-size: 14px; + line-height: 24px; + font-weight: 400px; + margin: 0; + display: block; + color: inherit; + text-decoration: none; +} + +.archived_on { + font-style: italic; + opacity: .8; + margin-top: -4px; +} + +.profile-grid { + display: grid; + grid-template-columns: 120px 1fr; + margin: var(--body-padding) calc(var(--body-padding) * -1); + font-size: 14px; +} + +.profile_property { + display: contents; + border-top: solid 1px gray; +} + +.profile_property .property_name { + background: lightgray; +} + +.profile_property > span { + border-top: solid 1px gray; + padding: var(--body-padding); +} + +.profile_property:last-child > span { + border-bottom: solid 1px gray; +} + +.profile_property .invisible { + display: none; +} + +.bio { + font-size: 14px; +} diff --git a/src/data.rs b/src/data.rs index e0181ce..d619bd9 100644 --- a/src/data.rs +++ b/src/data.rs @@ -1,3 +1,5 @@ +use std::str::FromStr; + use serde::Deserialize; #[derive(Deserialize, Debug, PartialEq, Clone)] @@ -20,6 +22,33 @@ pub struct Person { pub attachments: Option>, } +impl Person { + pub fn full_username(&self) -> Option { + let url = &self.url; + let domain_separator = url.find("//")?; + + if domain_separator + 2 >= url.len() { + return None; + } + + let mut parts = url.get((domain_separator + 2)..)?.split("/"); + let domain = parts.next()?; + let user = parts.next()?; + + if parts.count() > 0 { + return None; + } + + let mut username = String::new(); + + username.push_str(user); + username.push('@'); + username.push_str(domain); + + Some(username) + } +} + #[derive(Deserialize, Debug, PartialEq, Clone)] #[serde(tag = "type")] pub enum Item { diff --git a/src/render.rs b/src/render.rs index 34835e2..8f888bb 100644 --- a/src/render.rs +++ b/src/render.rs @@ -1,14 +1,14 @@ use chrono::DateTime; use futures::executor; -use yew::{function_component, html, Html, Renderer, ServerRenderer}; +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 wasm_bindgen::prelude::*; +use yew::prelude::*; use crate::{ - data::{Outbox, Person}, + data::{Attachment, Outbox, Person}, error::Ærror, }; @@ -76,10 +76,60 @@ fn Layout(props: &Props) -> Html { #[function_component] fn ProfileHeader(props: &Props) -> Html { html! { -

- {"Generated on "} - {props.archive_time.to_rfc2822()} -

+
+ + +

{props.author.name.as_str()}

+ + {props.author.full_username().unwrap().as_str()} + + { if props.author.attachments.is_some() { + html! { +
+ { + props.author.clone().attachments.unwrap().into_iter().filter_map(|attach| match attach { + Attachment::Property {name, value} => Some((name, value)), + _ => None, + }).map(|(name, value)| { + html! { +
+ {name} + { + if value.find(" +
+ } + }).collect::() + } +
+ } + } else { + html! { + <> + } + }} +
+ {Html::from_html_unchecked( + AttrValue::from( + props.author.summary.replace("class=\"u-url mention\"", "class=\"mention\" target=\"_blank\"") + ) + )} +
+ + {"Archived "} + {props.archive_time.format("%b %e, %Y")} + +
} } +#[function_component] +fn Posts(props: &Props) -> Html { + html! { +

{"Posts"}

+ } +}