diff options
| author | WriterV2 <primary-v2@protonmail.com> | 2026-05-11 23:52:49 +0200 |
|---|---|---|
| committer | WriterV2 <primary-v2@protonmail.com> | 2026-05-11 23:52:49 +0200 |
| commit | 5760f597eb17eab5abe6a3ae6c905ad9e111dc46 (patch) | |
| tree | 853c9062cbc911f9ebe287eba3210ecde5721949 /src/story.rs | |
| parent | 79618992153be5348f02ad91c8b91bd883c0b295 (diff) | |
Refactor page and story
Diffstat (limited to 'src/story.rs')
| -rw-r--r-- | src/story.rs | 187 |
1 files changed, 187 insertions, 0 deletions
diff --git a/src/story.rs b/src/story.rs new file mode 100644 index 0000000..c5371ee --- /dev/null +++ b/src/story.rs @@ -0,0 +1,187 @@ +use crate::page; +use anyhow::Result; +use maud::{html, Markup}; +use serde::Deserialize; +use toml::value::Date; + +impl page::Page for Vec<Story> { + fn page_title() -> String { + String::from("Stories") + } + + fn render_content(&self) -> Markup { + html!( + @for story in self { + (story.render_as_card()) + } + ) + } + + fn prepare_content(&mut self) -> Result<()> { + self.sort_by(|a, b| { + let date1 = a.update_date.unwrap_or(a.upload_date); + let date2 = b.update_date.unwrap_or(b.upload_date); + date2.cmp(&date1) + }); + for story in self { + std::fs::copy(&story.path, story.format_local_pdf_path())?; + } + Ok(()) + } + + fn content_style() -> Markup { + html!( + """ +h2 { + font-size: 1.8rem; + margin-bottom: 2rem; + color: #2F3C57; +} + +hr { + margin: 1rem; +} + + +.story-card { + text-align: left; + font-size: 1.2rem; + hyphens: auto; + ul { + display: flex; + justify-content: start; + list-style: none; + flex-wrap: wrap; + + li { + font-size: 1rem; + padding: 0.8rem; + margin: 0.8rem; + background-color: #2F3C57; + color: #ffd1a3; + outline: 0.2rem solid #2F3C57; + + a { + text-decoration: underline #E1CCEB; + color: #E1CCEB; + } + } + } + + hr { + margin: 2rem 0; + } + + p { + margin-top: 1rem; + } + + padding: 3rem; + margin: 3rem; + background-color: #ffd1a3; + border-bottom: 0.1rem dotted black; + + .content-warning { + background-color: #EC7981; + margin: 2rem 0; + padding: 0.3rem; + width: fit-content; + } +} + """ + ) + } +} + +#[derive(Deserialize, Debug)] +pub enum StoryLanguage { + German, + English, +} + +impl std::fmt::Display for StoryLanguage { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{:?}", self) + } +} + +#[derive(Deserialize)] +pub struct Story { + pub name: String, + pub description: String, + pub language: StoryLanguage, + pub upload_date: Date, + pub update_date: Option<Date>, + pub path: String, + pub warnings: Option<Vec<String>>, +} + +impl Story { + fn render_as_card(&self) -> Markup { + html!(div class="story-card" lang=(self.html_lang_value()) { + card { + h2 { (self.name) } + @if let Some(content_warnings) = &self.render_content_warnings() { + (content_warnings) + } + p { (self.description) } + hr {} + ul lang="en" { + li { "Language: " (self.language) } + li { "Uploaded on: " (self.upload_date) } + @if let Some(update_date) = self.update_date { + li { "Updated on: " (update_date) } + } + li { + a href=(&self.format_pdf_path()) download + { + "PDF" + } + } + } + } + }) + } + + pub fn format_pdf_path(&self) -> String { + let snake_case_name = self.name.to_lowercase().replace(" ", "_"); + format!("stories/{}.pdf", snake_case_name) + } + + fn format_local_pdf_path(&self) -> String { + format!("dist/{}", self.format_pdf_path()) + } + + pub fn html_lang_value(&self) -> String { + match self.language { + StoryLanguage::German => String::from("de"), + StoryLanguage::English => String::from("en"), + } + } + + pub fn render_content_warnings(&self) -> Option<Markup> { + if let Some(warnings) = &self.warnings { + let (summary, prefix) = match self.language { + StoryLanguage::German => ( + String::from("Vorsicht ist geboten."), + String::from("Die Geschichte beinhaltet: "), + ), + StoryLanguage::English => ( + String::from("Reader discretion is advised."), + String::from("The story contains: "), + ), + }; + let details = format!("{}{}", prefix, warnings.join(", ")); + Some(html!( + details { + summary { + strong { (summary) } + } + p class="content-warning" { (details) } + } + )) + } else { + None + } + } +} |
