Major refactoring on the front end. Dark mode is set to automatic again, in preparation for user preference management.
This commit is contained in:
parent
961bb61553
commit
8b04898c25
2
public/styles/tailwind.min.css
vendored
2
public/styles/tailwind.min.css
vendored
File diff suppressed because one or more lines are too long
@ -13,7 +13,8 @@ pub mod web_app_backend {
|
|||||||
use rocket::response::Redirect;
|
use rocket::response::Redirect;
|
||||||
use rocket::{Build, Rocket};
|
use rocket::{Build, Rocket};
|
||||||
use rocket_dyn_templates::{context, Template};
|
use rocket_dyn_templates::{context, Template};
|
||||||
use void_fe::void_app::{self, DarkModeProps, PoemRequest, VirtualDom};
|
use void_fe::void_app::{self, VirtualDom};
|
||||||
|
use void_fe::utils::prop_structs::{DarkModeProps, PoemRequest};
|
||||||
|
|
||||||
#[get("/")]
|
#[get("/")]
|
||||||
async fn index(cookies: &CookieJar<'_>) -> Template {
|
async fn index(cookies: &CookieJar<'_>) -> Template {
|
||||||
|
4
void-fe/src/components.rs
Normal file
4
void-fe/src/components.rs
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
pub mod void_poem;
|
||||||
|
pub mod void_footer;
|
||||||
|
pub mod void_title;
|
||||||
|
pub mod void_buttons;
|
49
void-fe/src/components/void_buttons.rs
Normal file
49
void-fe/src/components/void_buttons.rs
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
use dioxus::prelude::*;
|
||||||
|
use crate::utils::prop_structs::ButtonProps;
|
||||||
|
|
||||||
|
#[cfg(target_family = "wasm")]
|
||||||
|
use dioxus_router::Link;
|
||||||
|
|
||||||
|
pub fn BackToHomePage(cx: Scope) -> Element {
|
||||||
|
#[cfg(any(target_family = "windows", target_family = "unix"))]
|
||||||
|
return cx.render(rsx!{
|
||||||
|
a { class: "flex justify-center p-4 text-xl text-center ring-2 bg-alice-werefox-grey-lightest dark:bg-alice-werefox-grey-dark ring-alice-werefox-red-dark dark:ring-alice-werefox-red text-alice-werefox-grey-dark dark:text-alice-werefox-grey-light hover:text-alice-werefox-blue-dark dark:hover:text-alice-werefox-blue-light hover:ring-alice-werefox-blue dark:hover:ring-alice-werefox-blue hover:animate-yip transition",
|
||||||
|
href: "/",
|
||||||
|
p {
|
||||||
|
"Back to the homepage"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
#[cfg(target_family = "wasm")]
|
||||||
|
return cx.render(rsx!{
|
||||||
|
Link { class: "flex justify-center p-4 text-xl text-center ring-2 bg-alice-werefox-grey-lightest dark:bg-alice-werefox-grey-dark ring-alice-werefox-red-dark dark:ring-alice-werefox-red text-alice-werefox-grey-dark dark:text-alice-werefox-grey-light hover:text-alice-werefox-blue-dark dark:hover:text-alice-werefox-blue-light hover:ring-alice-werefox-blue dark:hover:ring-alice-werefox-blue hover:animate-yip transition",
|
||||||
|
to: "/",
|
||||||
|
p {
|
||||||
|
"Back to the homepage"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn NavigationButton(cx: Scope<ButtonProps>) -> Element {
|
||||||
|
let title = cx.props.title.clone();
|
||||||
|
let title_ref = title.as_str();
|
||||||
|
let slug = cx.props.slug.clone();
|
||||||
|
let slug_ref = slug.as_str();
|
||||||
|
#[cfg(any(target_family = "windows", target_family = "unix"))]
|
||||||
|
return cx.render(rsx!{
|
||||||
|
a { class: "flex mx-auto max-w-full justify-center p-4 ml-2 mr-2 text-xl text-center ring-2 bg-alice-werefox-grey-lightest dark:bg-alice-werefox-grey-dark ring-alice-werefox-red-dark dark:ring-alice-werefox-red text-alice-werefox-grey-dark dark:text-alice-werefox-grey-light hover:text-alice-werefox-blue-dark dark:hover:text-alice-werefox-blue-light hover:ring-alice-werefox-blue dark:hover:ring-alice-werefox-blue hover:animate-yip transition",
|
||||||
|
href: "{slug_ref}",
|
||||||
|
"{title_ref}"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
#[cfg(target_family = "wasm")]
|
||||||
|
return cx.render(rsx!{
|
||||||
|
Link { class: "flex mx-auto max-w-full justify-center p-4 ml-2 mr-2 text-xl text-center ring-2 bg-alice-werefox-grey-lightest dark:bg-alice-werefox-grey-dark ring-alice-werefox-red-dark dark:ring-alice-werefox-red text-alice-werefox-grey-dark dark:text-alice-werefox-grey-light hover:text-alice-werefox-blue-dark dark:hover:text-alice-werefox-blue-light hover:ring-alice-werefox-blue dark:hover:ring-alice-werefox-blue hover:animate-yip transition",
|
||||||
|
to: "{slug_ref}",
|
||||||
|
div {
|
||||||
|
dangerous_inner_html: "{title_ref}",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
15
void-fe/src/components/void_footer.rs
Normal file
15
void-fe/src/components/void_footer.rs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
use dioxus::prelude::*;
|
||||||
|
|
||||||
|
pub fn Footer(cx: Scope) -> Element {
|
||||||
|
cx.render(rsx!{
|
||||||
|
MutantStandardFooter {}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn MutantStandardFooter(cx: Scope) -> Element {
|
||||||
|
cx.render(rsx!{
|
||||||
|
div { class: "flex p-4 mx-auto max-w-full justify-center text-md text-center ring-4 bg-alice-werefox-grey-lightest dark:bg-alice-werefox-grey-dark ring-alice-werefox-red-dark dark:ring-alice-werefox-red text-alice-werefox-grey-dark dark:text-alice-werefox-grey-light",
|
||||||
|
"This site uses Mutant Standard emoji, which are licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License"
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
70
void-fe/src/components/void_poem.rs
Normal file
70
void-fe/src/components/void_poem.rs
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
use dioxus::prelude::*;
|
||||||
|
use crate::utils::helpers;
|
||||||
|
use crate::utils::prop_structs::{PoemChildren, PoemData};
|
||||||
|
use crate::components::void_buttons::*;
|
||||||
|
use crate::components::void_title::*;
|
||||||
|
|
||||||
|
pub fn PoemList(cx: Scope) -> Element {
|
||||||
|
let poem_list = helpers::get_poem_list();
|
||||||
|
cx.render(rsx! {
|
||||||
|
ul { class: "flex flex-col space-y-4",
|
||||||
|
poem_list.into_iter().map(|p| {
|
||||||
|
let slug = format!("/poems/{}", p.1);
|
||||||
|
rsx!{
|
||||||
|
NavigationButton { title: p.0, slug: slug }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn MakePoem<'a>(cx: Scope<'a, PoemChildren<'a>>) -> Element {
|
||||||
|
cx.render(rsx! {
|
||||||
|
div { class: "flex-col space-y-4",
|
||||||
|
&cx.props.children
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn GetPoem(cx: Scope<PoemData>) -> Element {
|
||||||
|
let slug = String::from(cx.props.slug.clone().expect("No slug specified."));
|
||||||
|
let (title, content, creation_date) = helpers::get_poem(slug.clone());
|
||||||
|
cx.render(rsx! {
|
||||||
|
Title { title: title, is_html: true }
|
||||||
|
MakePoem{
|
||||||
|
PoemContent { content: content, creation_date: creation_date }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn PoemContent(cx: Scope<PoemData>) -> Element {
|
||||||
|
let content = cx.props.content.clone().expect("No content specified.");
|
||||||
|
let creation_date = cx
|
||||||
|
.props
|
||||||
|
.creation_date
|
||||||
|
.clone()
|
||||||
|
.expect("No creation date specified.");
|
||||||
|
#[cfg(any(target_family = "unix", target_family = "windows"))]
|
||||||
|
return cx.render(rsx! {
|
||||||
|
div { class: "flex p-2 mx-auto max-w-full justify-center",
|
||||||
|
details { class: "group p-4 max-w-fit space-y-4 bg-alice-werefox-grey-lightest dark:bg-alice-werefox-grey-dark ring-4 ring-alice-werefox-red-dark dark:ring-alice-werefox-red text-alice-werefox-grey-dark dark:text-alice-werefox-grey-light",
|
||||||
|
summary { class: "group-open:before:content-['Close'] before:content-['Open'] flex justify-center p-2 ring-2 bg-alice-werefox-grey-lightest dark:bg-alice-werefox-grey-dark ring-alice-werefox-red-dark dark:ring-alice-werefox-red text-alice-werefox-grey-dark dark:text-alice-werefox-grey-light hover:text-alice-werefox-blue-dark dark:hover:text-alice-werefox-blue-light hover:ring-alice-werefox-blue dark:hover:ring-alice-werefox-blue hover:animate-yip transition",
|
||||||
|
}
|
||||||
|
div { class: "font-nerd flex flex-col space-y-4 py-4", "{content}{creation_date}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
#[cfg(target_family = "wasm")]
|
||||||
|
return cx.render(rsx! {
|
||||||
|
div { class: "flex p-2 mx-auto max-w-full justify-center",
|
||||||
|
details { class: "group p-4 max-w-fit space-y-4 bg-alice-werefox-grey-lightest dark:bg-alice-werefox-grey-dark ring-4 ring-alice-werefox-red-dark dark:ring-alice-werefox-red text-alice-werefox-grey-dark dark:text-alice-werefox-grey-light",
|
||||||
|
summary { class: "group-open:before:content-['Close'] before:content-['Open'] flex justify-center p-2 ring-2 bg-alice-werefox-grey-lightest dark:bg-alice-werefox-grey-dark ring-alice-werefox-red-dark dark:ring-alice-werefox-red text-alice-werefox-grey-dark dark:text-alice-werefox-grey-light hover:text-alice-werefox-blue-dark dark:hover:text-alice-werefox-blue-light hover:ring-alice-werefox-blue dark:hover:ring-alice-werefox-blue hover:animate-yip transition",
|
||||||
|
}
|
||||||
|
div { class: "font-nerd flex flex-col space-y-4 py-4",
|
||||||
|
dangerous_inner_html: "{content}{creation_date}",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
38
void-fe/src/components/void_title.rs
Normal file
38
void-fe/src/components/void_title.rs
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
use dioxus::prelude::*;
|
||||||
|
use crate::utils::prop_structs::TitleProps;
|
||||||
|
|
||||||
|
pub fn Title(cx: Scope<TitleProps>) -> Element {
|
||||||
|
let title = cx.props.title.clone();
|
||||||
|
let is_html = cx.props.is_html;
|
||||||
|
cx.render(rsx!{
|
||||||
|
div { class: "p-4 ring-4 bg-alice-werefox-grey-lightest dark:bg-alice-werefox-grey-dark ring-alice-werefox-red-dark dark:ring-alice-werefox-red text-alice-werefox-grey-dark dark:text-alice-werefox-grey-light",
|
||||||
|
p { class: "flex flex-row mx-auto max-w-full justify-center text-xl text-center",
|
||||||
|
TitleHtml { title: title, is_html: is_html }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn TitleHtml(cx: Scope<TitleProps>) -> Element {
|
||||||
|
let title = cx.props.title.clone();
|
||||||
|
if cx.props.is_html {
|
||||||
|
#[cfg(any(target_family = "unix", target_family = "windows"))]
|
||||||
|
return cx.render(rsx!{
|
||||||
|
span { class: "flex flex-row align-middle mx-auto max-w-full justify-center",
|
||||||
|
"{title} "
|
||||||
|
}
|
||||||
|
});
|
||||||
|
#[cfg(target_family = "wasm")]
|
||||||
|
return cx.render(rsx!{
|
||||||
|
span { class: "flex flex-row align-middle mx-auto max-w-full justify-center",
|
||||||
|
div { dangerous_inner_html: "{title}", }
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return cx.render(rsx!{
|
||||||
|
span {
|
||||||
|
"{title}"
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -4,102 +4,56 @@
|
|||||||
|
|
||||||
#![allow(non_snake_case)]
|
#![allow(non_snake_case)]
|
||||||
|
|
||||||
|
|
||||||
|
mod components;
|
||||||
|
pub mod utils;
|
||||||
|
|
||||||
|
|
||||||
/// A module that handles the functions needed
|
/// A module that handles the functions needed
|
||||||
/// to render the site.
|
/// to render the site.
|
||||||
pub mod void_app {
|
pub mod void_app {
|
||||||
// import the prelude to get access to the `rsx!` macro and the `Scope` and `Element` types
|
// import the prelude to get access to the `rsx!` macro and the `Scope` and `Element` types
|
||||||
pub use dioxus::prelude::*;
|
pub use dioxus::prelude::*;
|
||||||
use markdown::{self, Options};
|
|
||||||
use rust_embed::RustEmbed;
|
use rust_embed::RustEmbed;
|
||||||
use std::collections::VecDeque;
|
|
||||||
use dioxus_router::{Route, Router, Redirect};
|
use crate::components::void_buttons::*;
|
||||||
|
use crate::components::void_footer::*;
|
||||||
|
use crate::components::void_poem::*;
|
||||||
|
use crate::components::void_title::*;
|
||||||
|
use crate::utils::helpers;
|
||||||
|
use crate::utils::prop_structs::*;
|
||||||
|
|
||||||
#[cfg(any(target_family = "wasm"))]
|
#[cfg(any(target_family = "wasm"))]
|
||||||
use dioxus_helmet::Helmet;
|
use dioxus_helmet::Helmet;
|
||||||
#[cfg(any(target_family = "wasm"))]
|
#[cfg(any(target_family = "wasm"))]
|
||||||
use dioxus_router::{Link};
|
use dioxus_router::{Link, Route, Router, Redirect};
|
||||||
#[cfg(any(target_family = "wasm"))]
|
#[cfg(any(target_family = "wasm"))]
|
||||||
use dioxus_use_storage::use_local_storage;
|
use dioxus_use_storage::use_local_storage;
|
||||||
#[cfg(any(target_family = "wasm"))]
|
|
||||||
use serde::Deserialize;
|
|
||||||
|
|
||||||
#[cfg(target_family = "wasm")]
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
struct CurrentPoem {
|
|
||||||
current: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(PartialEq, Props)]
|
|
||||||
pub struct PoemRequest {
|
|
||||||
pub slug: String,
|
|
||||||
pub dark_mode: Option<bool>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(PartialEq, Props)]
|
|
||||||
pub struct DarkModeProps {
|
|
||||||
pub dark_mode: bool,
|
|
||||||
pub slug: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(PartialEq, Props)]
|
|
||||||
struct ButtonProps {
|
|
||||||
title: String,
|
|
||||||
slug: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(PartialEq, Props)]
|
|
||||||
struct PoemData {
|
|
||||||
title: Option<String>,
|
|
||||||
content: Option<String>,
|
|
||||||
creation_date: Option<String>,
|
|
||||||
slug: Option<String>,
|
|
||||||
dark_mode: Option<bool>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Props)]
|
|
||||||
struct PoemChildren<'a> {
|
|
||||||
children: Element<'a>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(RustEmbed)]
|
#[derive(RustEmbed)]
|
||||||
#[folder = "data/poems"]
|
#[folder = "data/poems"]
|
||||||
pub struct Poems;
|
pub struct Poems;
|
||||||
|
|
||||||
|
#[cfg(target_family = "wasm")]
|
||||||
pub fn DioxusApp(cx: Scope) -> Element {
|
pub fn DioxusApp(cx: Scope) -> Element {
|
||||||
// use dioxus_router::Redirect;
|
// use dioxus_router::Redirect;
|
||||||
|
|
||||||
cx.render(rsx! {
|
cx.render(rsx! {
|
||||||
Router {
|
Router {
|
||||||
Route { to: "/", HomePage { dark_mode: true, } }
|
Route { to: "/", self::HomePage { dark_mode: true, } }
|
||||||
Route { to: "/poems/", PoemListPage { slug: "".to_string(), dark_mode: true, } }
|
Route { to: "/poems",
|
||||||
Route { to: "/poems/:slug", PoemPage { slug: "".to_string(), dark_mode: true, } }
|
PoemListPage { slug: "".to_string(), dark_mode: true, }
|
||||||
|
}
|
||||||
|
Route { to: "/poems/:slug",
|
||||||
|
PoemPage { slug: "".to_string(), dark_mode: true, }
|
||||||
|
}
|
||||||
Route { to: "", PageNotFound {} }
|
Route { to: "", PageNotFound {} }
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_family = "wasm")]
|
#[cfg(target_family = "wasm")]
|
||||||
fn RoutePreviousPage(cx: Scope<PoemRequest>) -> Element {
|
|
||||||
let current = String::from(dioxus_router::use_route(cx)
|
|
||||||
.segment("slug")
|
|
||||||
.expect("Slug to know which poem we are at."));
|
|
||||||
log::info!("{}", current.clone());
|
|
||||||
let previous = current.clone();
|
|
||||||
cx.render(rsx!{
|
|
||||||
Redirect { to: "{previous}" }
|
|
||||||
})
|
|
||||||
}
|
|
||||||
#[cfg(target_family = "wasm")]
|
|
||||||
fn RouteNextPage(cx: Scope<PoemRequest>) -> Element {
|
|
||||||
let current = String::from(dioxus_router::use_route(cx)
|
|
||||||
.segment("slug")
|
|
||||||
.expect("Slug to know which poem we are at."));
|
|
||||||
let next = current.clone();
|
|
||||||
cx.render(rsx!{
|
|
||||||
Redirect { to: "{next}" }
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn PageNotFound(cx: Scope) -> Element {
|
fn PageNotFound(cx: Scope) -> Element {
|
||||||
cx.render(rsx! {
|
cx.render(rsx! {
|
||||||
p { "That page doesn't exist, sorry!" }
|
p { "That page doesn't exist, sorry!" }
|
||||||
@ -107,132 +61,6 @@ pub mod void_app {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn MutantStandardFooter(cx: Scope) -> Element {
|
|
||||||
cx.render(rsx!{
|
|
||||||
div { class: "flex p-4 mx-auto max-w-full justify-center text-md text-center ring-4 bg-alice-werefox-grey-lightest dark:bg-alice-werefox-grey-dark ring-alice-werefox-red-dark dark:ring-alice-werefox-red text-alice-werefox-grey-dark dark:text-alice-werefox-grey-light",
|
|
||||||
"This site uses Mutant Standard emoji, which are licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License"
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn BackToHomePage(cx: Scope) -> Element {
|
|
||||||
#[cfg(any(target_family = "windows", target_family = "unix"))]
|
|
||||||
return cx.render(rsx!{
|
|
||||||
a { class: "flex justify-center p-4 text-xl text-center ring-2 bg-alice-werefox-grey-lightest dark:bg-alice-werefox-grey-dark ring-alice-werefox-red-dark dark:ring-alice-werefox-red text-alice-werefox-grey-dark dark:text-alice-werefox-grey-light hover:text-alice-werefox-blue-dark dark:hover:text-alice-werefox-blue-light hover:ring-alice-werefox-blue dark:hover:ring-alice-werefox-blue hover:animate-yip transition",
|
|
||||||
href: "/",
|
|
||||||
p {
|
|
||||||
"Back to the homepage"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
#[cfg(target_family = "wasm")]
|
|
||||||
return cx.render(rsx!{
|
|
||||||
Link { class: "flex justify-center p-4 text-xl text-center ring-2 bg-alice-werefox-grey-lightest dark:bg-alice-werefox-grey-dark ring-alice-werefox-red-dark dark:ring-alice-werefox-red text-alice-werefox-grey-dark dark:text-alice-werefox-grey-light hover:text-alice-werefox-blue-dark dark:hover:text-alice-werefox-blue-light hover:ring-alice-werefox-blue dark:hover:ring-alice-werefox-blue hover:animate-yip transition",
|
|
||||||
to: "/",
|
|
||||||
p {
|
|
||||||
"Back to the homepage"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn NavigationButton(cx: Scope<ButtonProps>) -> Element {
|
|
||||||
let title = cx.props.title.clone();
|
|
||||||
let title_ref = title.as_str();
|
|
||||||
let slug = cx.props.slug.clone();
|
|
||||||
let slug_ref = slug.as_str();
|
|
||||||
#[cfg(any(target_family = "windows", target_family = "unix"))]
|
|
||||||
return cx.render(rsx!{
|
|
||||||
a { class: "flex mx-auto max-w-full justify-center p-4 ml-2 mr-2 text-xl text-center ring-2 bg-alice-werefox-grey-lightest dark:bg-alice-werefox-grey-dark ring-alice-werefox-red-dark dark:ring-alice-werefox-red text-alice-werefox-grey-dark dark:text-alice-werefox-grey-light hover:text-alice-werefox-blue-dark dark:hover:text-alice-werefox-blue-light hover:ring-alice-werefox-blue dark:hover:ring-alice-werefox-blue hover:animate-yip transition",
|
|
||||||
href: "{slug_ref}",
|
|
||||||
"{title_ref}"
|
|
||||||
}
|
|
||||||
});
|
|
||||||
#[cfg(target_family = "wasm")]
|
|
||||||
return cx.render(rsx!{
|
|
||||||
Link { class: "flex mx-auto max-w-full justify-center p-4 ml-2 mr-2 text-xl text-center ring-2 bg-alice-werefox-grey-lightest dark:bg-alice-werefox-grey-dark ring-alice-werefox-red-dark dark:ring-alice-werefox-red text-alice-werefox-grey-dark dark:text-alice-werefox-grey-light hover:text-alice-werefox-blue-dark dark:hover:text-alice-werefox-blue-light hover:ring-alice-werefox-blue dark:hover:ring-alice-werefox-blue hover:animate-yip transition",
|
|
||||||
to: "{slug_ref}",
|
|
||||||
div {
|
|
||||||
dangerous_inner_html: "{title_ref}",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn DarkModeButton(cx: Scope<DarkModeProps>) -> Element {
|
|
||||||
let slug = cx
|
|
||||||
.props
|
|
||||||
.slug
|
|
||||||
.clone()
|
|
||||||
.expect("Slug found to know where to redirect after dark mode setting.");
|
|
||||||
let slug_ref = slug.as_str();
|
|
||||||
let dark_mode = cx.props.dark_mode;
|
|
||||||
#[cfg(any(target_family = "windows", target_family = "unix"))]
|
|
||||||
return cx.render(rsx! {
|
|
||||||
a { href: "/?dark_mode&callback={slug_ref}",
|
|
||||||
match dark_mode {
|
|
||||||
true => {
|
|
||||||
rsx! {
|
|
||||||
img { src: "/images/white_square_button.png",
|
|
||||||
alt: "A white square button that can toggle dark mode.",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
false => {
|
|
||||||
rsx! {
|
|
||||||
img { src: "/images/black_square_button.png",
|
|
||||||
alt: "A black square button that can toggle dark mode.",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
#[cfg(target_family = "wasm")]
|
|
||||||
return cx.render(rsx! {
|
|
||||||
Link { to: "{slug_ref}/toggle-dark-mode",
|
|
||||||
match dark_mode {
|
|
||||||
true => {
|
|
||||||
rsx! {
|
|
||||||
img { src: "/images/white_square_button.png",
|
|
||||||
alt: "A white square button that can toggle dark mode.",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
false => {
|
|
||||||
rsx! {
|
|
||||||
img { src: "/images/black_square_button.png",
|
|
||||||
alt: "A black square button that can toggle dark mode.",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn PoemButton(cx: Scope<ButtonProps>) -> Element {
|
|
||||||
let title = cx.props.title.clone();
|
|
||||||
let title_ref = title.as_str();
|
|
||||||
let slug = cx.props.slug.clone();
|
|
||||||
let slug_ref = slug.as_str();
|
|
||||||
#[cfg(any(target_family = "unix", target_family = "windows"))]
|
|
||||||
return cx.render(rsx!{
|
|
||||||
a { href: "/poems/{slug_ref}",
|
|
||||||
li { class: "p-4 ml-2 mr-2 text-xl text-center ring-2 bg-alice-werefox-grey-lightest dark:bg-alice-werefox-grey-dark ring-alice-werefox-red-dark dark:ring-alice-werefox-red text-alice-werefox-grey-dark dark:text-alice-werefox-grey-light hover:text-alice-werefox-blue-dark dark:hover:text-alice-werefox-blue-light hover:ring-alice-werefox-blue dark:hover:ring-alice-werefox-blue hover:animate-yip transition",
|
|
||||||
"{title_ref}"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
#[cfg(target_family = "wasm")]
|
|
||||||
return cx.render(rsx!{
|
|
||||||
Link { to: "/poems/{slug_ref}",
|
|
||||||
li { class: "p-4 ml-2 mr-2 text-xl text-center ring-2 bg-alice-werefox-grey-lightest dark:bg-alice-werefox-grey-dark ring-alice-werefox-red-dark dark:ring-alice-werefox-red text-alice-werefox-grey-dark dark:text-alice-werefox-grey-light hover:text-alice-werefox-blue-dark dark:hover:text-alice-werefox-blue-light hover:ring-alice-werefox-blue dark:hover:ring-alice-werefox-blue hover:animate-yip transition",
|
|
||||||
dangerous_inner_html: "{title_ref}",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Renders the app and returns the rendered Element.
|
/// Renders the app and returns the rendered Element.
|
||||||
pub fn HomePage(cx: Scope<DarkModeProps>) -> Element {
|
pub fn HomePage(cx: Scope<DarkModeProps>) -> Element {
|
||||||
@ -240,19 +68,11 @@ pub mod void_app {
|
|||||||
let slug = cx.props.slug.clone().expect("Slug for dark mode redirect.");
|
let slug = cx.props.slug.clone().expect("Slug for dark mode redirect.");
|
||||||
#[cfg(target_family = "wasm")]
|
#[cfg(target_family = "wasm")]
|
||||||
let slug = "".to_string();
|
let slug = "".to_string();
|
||||||
let oldest_uri = format!("/poems/{}", get_oldest_entry());
|
|
||||||
let latest_uri = format!("/poems/{}", get_latest_entry());
|
|
||||||
let title = "A Letter to the Void".to_string();
|
let title = "A Letter to the Void".to_string();
|
||||||
let dark_mode = cx.props.dark_mode.clone();
|
|
||||||
cx.render(rsx!{
|
cx.render(rsx!{
|
||||||
div { class: "min-h-screen font-nerd bg-alice-werefox-grey-light dark:bg-alice-werefox-grey",
|
div { class: "min-h-screen font-nerd bg-alice-werefox-grey-light dark:bg-alice-werefox-grey",
|
||||||
div { class: "container space-y-4 mx-auto p-4",
|
div { class: "container space-y-4 mx-auto p-4",
|
||||||
div { class: "p-4 ring-4 bg-alice-werefox-grey-lightest dark:bg-alice-werefox-grey-dark ring-alice-werefox-red-dark dark:ring-alice-werefox-red text-alice-werefox-grey-dark dark:text-alice-werefox-grey-light",
|
Title { title: title, is_html: false }
|
||||||
p { class: "flex flex-row mx-auto max-w-full justify-center text-xl text-center",
|
|
||||||
"{title}"
|
|
||||||
DarkModeButton { slug: slug, dark_mode: dark_mode }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
div { class: "flex p-4 ml-2 mr-2 ring-4 bg-alice-werefox-grey-lightest dark:bg-alice-werefox-grey-dark ring-alice-werefox-red-dark dark:ring-alice-werefox-red text-alice-werefox-grey-dark dark:text-alice-werefox-grey-light",
|
div { class: "flex p-4 ml-2 mr-2 ring-4 bg-alice-werefox-grey-lightest dark:bg-alice-werefox-grey-dark ring-alice-werefox-red-dark dark:ring-alice-werefox-red text-alice-werefox-grey-dark dark:text-alice-werefox-grey-light",
|
||||||
p { class: "text-lg text-center",
|
p { class: "text-lg text-center",
|
||||||
"Welcome, and I hope you enjoy your stay!"
|
"Welcome, and I hope you enjoy your stay!"
|
||||||
@ -267,112 +87,30 @@ pub mod void_app {
|
|||||||
"🖤 Alice Icehart Werefox"
|
"🖤 Alice Icehart Werefox"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
NavigationButton { title: "See Latest Entry".to_string(), slug: latest_uri }
|
NavigationButton { title: "See Latest Entry".to_string(), slug: helpers::get_latest_entry(slug.clone()) }
|
||||||
NavigationButton { title: "See Oldest Entry".to_string(), slug: oldest_uri }
|
NavigationButton { title: "See Oldest Entry".to_string(), slug: helpers::get_oldest_entry(slug.clone()) }
|
||||||
NavigationButton { title: "See All Entries".to_string(), slug: "/poems".to_string() }
|
NavigationButton { title: "See All Entries".to_string(), slug: "/poems".to_string() }
|
||||||
MutantStandardFooter {}
|
Footer {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_poem_list() -> Vec<(String, String)> {
|
|
||||||
let mut poem_list = Vec::new();
|
|
||||||
for p in Poems::iter() {
|
|
||||||
let filename = p.to_string();
|
|
||||||
let poem_content = Poems::get(&filename).expect("Found poem {filename:?}");
|
|
||||||
let mut poem_to_str = std::str::from_utf8(poem_content.data.as_ref())
|
|
||||||
.expect("Title is valid UT8.")
|
|
||||||
.lines();
|
|
||||||
let title_markdown = poem_to_str.next().expect("No title specified.");
|
|
||||||
let title = markdown::to_html_with_options(title_markdown, &Options::gfm()).unwrap();
|
|
||||||
let slug = String::from(filename.trim_end_matches(".md"));
|
|
||||||
poem_list.push((title.clone(), slug.clone()));
|
|
||||||
}
|
|
||||||
log::trace!("{poem_list:?}");
|
|
||||||
poem_list
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_oldest_entry() -> String {
|
|
||||||
let mut poem_list = VecDeque::from(get_poem_list());
|
|
||||||
poem_list
|
|
||||||
.pop_front()
|
|
||||||
.expect("There is an entry in this list of poems.")
|
|
||||||
.1
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_latest_entry() -> String {
|
|
||||||
let mut poem_list = get_poem_list();
|
|
||||||
poem_list
|
|
||||||
.pop()
|
|
||||||
.expect("There is an entry in this list of poems.")
|
|
||||||
.1
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_previous_entry(slug: String) -> Option<String> {
|
|
||||||
let poem_list = get_poem_list();
|
|
||||||
poem_list.iter().enumerate().find_map(|(index, p)| {
|
|
||||||
if p.1 == slug {
|
|
||||||
if index != 0 {
|
|
||||||
Some(poem_list[index - 1].1.clone())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_next_entry(slug: String) -> Option<String> {
|
|
||||||
let poem_list = get_poem_list();
|
|
||||||
poem_list.iter().enumerate().find_map(|(index, p)| {
|
|
||||||
if p.1 == slug {
|
|
||||||
if index != poem_list.len() - 1 {
|
|
||||||
Some(poem_list[index + 1].1.clone())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
/// Renders the app and returns the rendered Element.
|
/// Renders the app and returns the rendered Element.
|
||||||
pub fn PoemListPage(cx: Scope<DarkModeProps>) -> Element {
|
pub fn PoemListPage(cx: Scope<DarkModeProps>) -> Element {
|
||||||
let slug = cx.props.slug.clone().expect("Slug for dark mode redirect.");
|
|
||||||
let dark_mode = cx.props.dark_mode.clone();
|
|
||||||
cx.render(rsx!{
|
cx.render(rsx!{
|
||||||
div { class: "min-h-screen font-nerd bg-alice-werefox-grey-light dark:bg-alice-werefox-grey",
|
div { class: "min-h-screen font-nerd bg-alice-werefox-grey-light dark:bg-alice-werefox-grey",
|
||||||
div { class: "container space-y-4 mx-auto p-4",
|
div { class: "container space-y-4 mx-auto p-4",
|
||||||
div { class: "p-4 ring-4 bg-alice-werefox-grey-lightest dark:bg-alice-werefox-grey-dark ring-alice-werefox-red-dark dark:ring-alice-werefox-red text-alice-werefox-grey-dark dark:text-alice-werefox-grey-light",
|
Title { title: "A Letter to the Void".to_string(), is_html: false }
|
||||||
p { class: "flex flex-row mx-auto max-w-full justify-center text-lg text-center",
|
|
||||||
"A Letter to the Void "
|
|
||||||
DarkModeButton { slug: slug, dark_mode: dark_mode }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
BackToHomePage {}
|
BackToHomePage {}
|
||||||
PoemList {}
|
PoemList {}
|
||||||
BackToHomePage {}
|
BackToHomePage {}
|
||||||
MutantStandardFooter {}
|
Footer {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn PoemList(cx: Scope) -> Element {
|
|
||||||
let poem_list = get_poem_list();
|
|
||||||
cx.render(rsx! {
|
|
||||||
ul { class: "flex flex-col space-y-4",
|
|
||||||
poem_list.into_iter().map(|p| {
|
|
||||||
rsx!{
|
|
||||||
PoemButton { title: p.0, slug: p.1 }
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn PoemPage(cx: Scope<PoemRequest>) -> Element {
|
pub fn PoemPage(cx: Scope<PoemRequest>) -> Element {
|
||||||
#[cfg(any(target_family = "unix", target_family = "windows"))]
|
#[cfg(any(target_family = "unix", target_family = "windows"))]
|
||||||
let slug = cx.props.slug.clone();
|
let slug = cx.props.slug.clone();
|
||||||
@ -382,22 +120,6 @@ pub mod void_app {
|
|||||||
.segment("slug")
|
.segment("slug")
|
||||||
.expect("No slug specified."),
|
.expect("No slug specified."),
|
||||||
);
|
);
|
||||||
let mut oldest_uri = get_oldest_entry();
|
|
||||||
if oldest_uri == slug.to_string() {
|
|
||||||
oldest_uri = format!("/poems/{slug}#");
|
|
||||||
}
|
|
||||||
let previous_uri = match get_previous_entry(slug.clone()) {
|
|
||||||
Some(p) => format!("/poems/{p}"),
|
|
||||||
None => format!("/poems/{slug}#"),
|
|
||||||
};
|
|
||||||
let next_uri = match get_next_entry(slug.clone()) {
|
|
||||||
Some(p) => format!("/poems/{p}"),
|
|
||||||
None => format!("/poems/{slug}#"),
|
|
||||||
};
|
|
||||||
let mut latest_uri = get_latest_entry();
|
|
||||||
if latest_uri == slug.to_string() {
|
|
||||||
latest_uri = format!("{slug}#");
|
|
||||||
}
|
|
||||||
let dark_mode = cx
|
let dark_mode = cx
|
||||||
.props
|
.props
|
||||||
.dark_mode
|
.dark_mode
|
||||||
@ -407,115 +129,17 @@ pub mod void_app {
|
|||||||
div { class: "min-h-screen font-nerd bg-alice-werefox-grey-light dark:bg-alice-werefox-grey",
|
div { class: "min-h-screen font-nerd bg-alice-werefox-grey-light dark:bg-alice-werefox-grey",
|
||||||
div { class: "container space-y-4 mx-auto p-4",
|
div { class: "container space-y-4 mx-auto p-4",
|
||||||
BackToHomePage {}
|
BackToHomePage {}
|
||||||
GetPoem { slug: slug, dark_mode: dark_mode }
|
GetPoem { slug: slug.clone(), dark_mode: dark_mode }
|
||||||
div { class: "grid md:grid-cols-4 md:grid-rows-1 grid-cols-1 grid-rows-4 gap-y-4",
|
div { class: "grid md:grid-cols-4 md:grid-rows-1 grid-cols-1 grid-rows-4 gap-y-4",
|
||||||
NavigationButton { title: "Oldest".to_string(), slug: oldest_uri }
|
NavigationButton { title: "Oldest".to_string(), slug: helpers::get_oldest_entry(slug.clone()) }
|
||||||
NavigationButton { title: "Previous".to_string(), slug: previous_uri }
|
NavigationButton { title: "Previous".to_string(), slug: helpers::get_previous_entry(slug.clone()) }
|
||||||
NavigationButton { title: "Next".to_string(), slug: next_uri }
|
NavigationButton { title: "Next".to_string(), slug: helpers::get_next_entry(slug.clone()) }
|
||||||
NavigationButton { title: "Latest".to_string(), slug: latest_uri }
|
NavigationButton { title: "Latest".to_string(), slug: helpers::get_latest_entry(slug.clone()) }
|
||||||
}
|
}
|
||||||
BackToHomePage {}
|
BackToHomePage {}
|
||||||
MutantStandardFooter {}
|
Footer {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn GetPoem(cx: Scope<PoemData>) -> Element {
|
|
||||||
// It would be good to implement some kind of "get_poem_data" or "into" functionality for the struct, so I don't have to write all this here.
|
|
||||||
let dark_mode = cx
|
|
||||||
.props
|
|
||||||
.dark_mode
|
|
||||||
.clone()
|
|
||||||
.expect("Dark mode prop not passed.");
|
|
||||||
let slug = String::from(cx.props.slug.clone().expect("No slug specified."));
|
|
||||||
let filename = String::from(String::from(slug.clone()) + ".md");
|
|
||||||
let creation_date =
|
|
||||||
String::from(String::from("<br>Written on: ") + filename.split("_").next().unwrap());
|
|
||||||
let poem_content = Poems::get(&filename).expect("Found poem {filename:?}");
|
|
||||||
let mut poem_to_str = std::str::from_utf8(poem_content.data.as_ref())
|
|
||||||
.expect("Title is valid UT8.")
|
|
||||||
.lines();
|
|
||||||
let poem_title = poem_to_str.next().unwrap();
|
|
||||||
let poem_content = poem_to_str.into_iter().collect::<Vec<&str>>().join("\n");
|
|
||||||
let poem_title_to_html_string =
|
|
||||||
markdown::to_html_with_options(poem_title, &Options::gfm()).unwrap();
|
|
||||||
let poem_content_to_html_string =
|
|
||||||
markdown::to_html_with_options(poem_content.as_str(), &Options::gfm()).unwrap();
|
|
||||||
cx.render(rsx! {
|
|
||||||
MakePoem{
|
|
||||||
RenderPoemTitle { title: poem_title_to_html_string, slug: slug, dark_mode: dark_mode }
|
|
||||||
RenderPoemElement { content: poem_content_to_html_string, creation_date: creation_date }
|
|
||||||
}})
|
|
||||||
}
|
|
||||||
|
|
||||||
// #[cfg(any(target_family = "unix", target_family = "windows"))]
|
|
||||||
fn RenderPoemTitle(cx: Scope<PoemData>) -> Element {
|
|
||||||
let slug = cx.props.slug.clone().expect("Slug prop was not passed.");
|
|
||||||
let callback = format!("/poems/{slug}");
|
|
||||||
let dark_mode = cx
|
|
||||||
.props
|
|
||||||
.dark_mode
|
|
||||||
.clone()
|
|
||||||
.expect("Dark mode prop not passed.");
|
|
||||||
let title = cx.props.title.clone().expect("No title specified.");
|
|
||||||
#[cfg(any(target_family = "unix", target_family = "windows"))]
|
|
||||||
return cx.render(rsx!{
|
|
||||||
span { class: "p-4 ml-2 mr-2 flex text-xl text-center ring-4 bg-alice-werefox-grey-lightest dark:bg-alice-werefox-grey-dark ring-alice-werefox-red-dark dark:ring-alice-werefox-red text-alice-werefox-grey-dark dark:text-alice-werefox-grey-light",
|
|
||||||
span { class: "flex flex-row align-middle mx-auto max-w-full justify-center",
|
|
||||||
"{title} "
|
|
||||||
DarkModeButton { slug: callback, dark_mode: dark_mode }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
#[cfg(target_family = "wasm")]
|
|
||||||
return cx.render(rsx!{
|
|
||||||
span { class: "p-4 ml-2 mr-2 flex text-xl text-center ring-4 bg-alice-werefox-grey-lightest dark:bg-alice-werefox-grey-dark ring-alice-werefox-red-dark dark:ring-alice-werefox-red text-alice-werefox-grey-dark dark:text-alice-werefox-grey-light",
|
|
||||||
span { class: "flex flex-row align-middle mx-auto max-w-full justify-center",
|
|
||||||
div { dangerous_inner_html: "{title}", }
|
|
||||||
DarkModeButton { slug: callback, dark_mode: dark_mode }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn RenderPoemElement(cx: Scope<PoemData>) -> Element {
|
|
||||||
let content = cx.props.content.clone().expect("No content specified.");
|
|
||||||
let creation_date = cx
|
|
||||||
.props
|
|
||||||
.creation_date
|
|
||||||
.clone()
|
|
||||||
.expect("No creation date specified.");
|
|
||||||
#[cfg(any(target_family = "unix", target_family = "windows"))]
|
|
||||||
return cx.render(rsx! {
|
|
||||||
div { class: "flex p-2 mx-auto max-w-full justify-center",
|
|
||||||
details { class: "group p-4 max-w-fit space-y-4 bg-alice-werefox-grey-lightest dark:bg-alice-werefox-grey-dark ring-4 ring-alice-werefox-red-dark dark:ring-alice-werefox-red text-alice-werefox-grey-dark dark:text-alice-werefox-grey-light",
|
|
||||||
summary { class: "group-open:before:content-['Close'] before:content-['Open'] flex justify-center p-2 ring-2 bg-alice-werefox-grey-lightest dark:bg-alice-werefox-grey-dark ring-alice-werefox-red-dark dark:ring-alice-werefox-red text-alice-werefox-grey-dark dark:text-alice-werefox-grey-light hover:text-alice-werefox-blue-dark dark:hover:text-alice-werefox-blue-light hover:ring-alice-werefox-blue dark:hover:ring-alice-werefox-blue hover:animate-yip transition",
|
|
||||||
}
|
|
||||||
div { class: "font-nerd flex flex-col space-y-4 py-4", "{content}{creation_date}"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
#[cfg(target_family = "wasm")]
|
|
||||||
return cx.render(rsx! {
|
|
||||||
div { class: "flex p-2 mx-auto max-w-full justify-center",
|
|
||||||
details { class: "group p-4 max-w-fit space-y-4 bg-alice-werefox-grey-lightest dark:bg-alice-werefox-grey-dark ring-4 ring-alice-werefox-red-dark dark:ring-alice-werefox-red text-alice-werefox-grey-dark dark:text-alice-werefox-grey-light",
|
|
||||||
summary { class: "group-open:before:content-['Close'] before:content-['Open'] flex justify-center p-2 ring-2 bg-alice-werefox-grey-lightest dark:bg-alice-werefox-grey-dark ring-alice-werefox-red-dark dark:ring-alice-werefox-red text-alice-werefox-grey-dark dark:text-alice-werefox-grey-light hover:text-alice-werefox-blue-dark dark:hover:text-alice-werefox-blue-light hover:ring-alice-werefox-blue dark:hover:ring-alice-werefox-blue hover:animate-yip transition",
|
|
||||||
}
|
|
||||||
div { class: "font-nerd flex flex-col space-y-4 py-4",
|
|
||||||
dangerous_inner_html: "{content}{creation_date}",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn MakePoem<'a>(cx: Scope<'a, PoemChildren<'a>>) -> Element {
|
|
||||||
cx.render(rsx! {
|
|
||||||
div { class: "flex-col space-y-4",
|
|
||||||
&cx.props.children
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
3
void-fe/src/utils.rs
Normal file
3
void-fe/src/utils.rs
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
pub mod helpers;
|
||||||
|
pub mod prop_structs;
|
||||||
|
pub mod user_prefs;
|
97
void-fe/src/utils/helpers.rs
Normal file
97
void-fe/src/utils/helpers.rs
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
use std::collections::VecDeque;
|
||||||
|
use crate::void_app::Poems;
|
||||||
|
use markdown::Options;
|
||||||
|
|
||||||
|
pub fn get_poem(slug: String) -> (String, String, String) {
|
||||||
|
let filename = String::from(String::from(slug.clone()) + ".md");
|
||||||
|
let creation_date =
|
||||||
|
String::from(String::from("<br>Written on: ") + filename.split("_").next().unwrap());
|
||||||
|
let poem_content = Poems::get(&filename).expect("Found poem {filename:?}");
|
||||||
|
let mut poem_to_str = std::str::from_utf8(poem_content.data.as_ref())
|
||||||
|
.expect("Title is valid UT8.")
|
||||||
|
.lines();
|
||||||
|
let poem_title = poem_to_str.next().unwrap();
|
||||||
|
let poem_content = poem_to_str.into_iter().collect::<Vec<&str>>().join("\n");
|
||||||
|
let poem_title_to_html_string =
|
||||||
|
markdown::to_html_with_options(poem_title, &Options::gfm()).unwrap();
|
||||||
|
let poem_content_to_html_string =
|
||||||
|
markdown::to_html_with_options(poem_content.as_str(), &Options::gfm()).unwrap();
|
||||||
|
(poem_title_to_html_string, poem_content_to_html_string, creation_date)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_poem_list() -> Vec<(String, String)> {
|
||||||
|
let mut poem_list = Vec::new();
|
||||||
|
for p in Poems::iter() {
|
||||||
|
let filename = p.to_string();
|
||||||
|
let poem_content = Poems::get(&filename).expect("Found poem {filename:?}");
|
||||||
|
let mut poem_to_str = std::str::from_utf8(poem_content.data.as_ref())
|
||||||
|
.expect("Title is valid UT8.")
|
||||||
|
.lines();
|
||||||
|
let title_markdown = poem_to_str.next().expect("No title specified.");
|
||||||
|
let title = markdown::to_html_with_options(title_markdown, &Options::gfm()).unwrap();
|
||||||
|
let slug = String::from(filename.trim_end_matches(".md"));
|
||||||
|
poem_list.push((title.clone(), slug.clone()));
|
||||||
|
}
|
||||||
|
log::trace!("{poem_list:?}");
|
||||||
|
poem_list
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_oldest_entry(current: String) -> String {
|
||||||
|
let mut poem_list = VecDeque::from(get_poem_list());
|
||||||
|
let oldest = poem_list
|
||||||
|
.pop_front()
|
||||||
|
.expect("There is an entry in this list of poems.")
|
||||||
|
.1;
|
||||||
|
if current == oldest {
|
||||||
|
return format!("/poems/{current}#");
|
||||||
|
}
|
||||||
|
format!("/poems/{oldest}")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_latest_entry(current: String) -> String {
|
||||||
|
let mut poem_list = get_poem_list();
|
||||||
|
let latest = poem_list
|
||||||
|
.pop()
|
||||||
|
.expect("There is an entry in this list of poems.")
|
||||||
|
.1;
|
||||||
|
if current == latest {
|
||||||
|
return format!("/poems/{current}#");
|
||||||
|
}
|
||||||
|
format!("/poems/{latest}")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_previous_entry(current: String) -> String {
|
||||||
|
let poem_list = get_poem_list();
|
||||||
|
match poem_list.iter().enumerate().find_map(|(index, p)| {
|
||||||
|
if p.1 == current {
|
||||||
|
if index != 0 {
|
||||||
|
Some(poem_list[index - 1].1.clone())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}) {
|
||||||
|
Some(entry) => format!("/poems/{entry}"),
|
||||||
|
None => format!("/poems/{current}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_next_entry(current: String) -> String {
|
||||||
|
let poem_list = get_poem_list();
|
||||||
|
match poem_list.iter().enumerate().find_map(|(index, p)| {
|
||||||
|
if p.1 == current {
|
||||||
|
if index != poem_list.len() - 1 {
|
||||||
|
Some(poem_list[index + 1].1.clone())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}) {
|
||||||
|
Some(entry) => format!("/poems/{entry}"),
|
||||||
|
None => format!("/poems/{current}"),
|
||||||
|
}
|
||||||
|
}
|
39
void-fe/src/utils/prop_structs.rs
Normal file
39
void-fe/src/utils/prop_structs.rs
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
use crate::void_app::{Element, Props};
|
||||||
|
|
||||||
|
#[derive(PartialEq, Props)]
|
||||||
|
pub struct PoemRequest {
|
||||||
|
pub slug: String,
|
||||||
|
pub dark_mode: Option<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Props)]
|
||||||
|
pub struct DarkModeProps {
|
||||||
|
pub dark_mode: bool,
|
||||||
|
pub slug: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Props)]
|
||||||
|
pub struct TitleProps {
|
||||||
|
pub title: String,
|
||||||
|
pub is_html: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Props)]
|
||||||
|
pub struct ButtonProps {
|
||||||
|
pub title: String,
|
||||||
|
pub slug: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Props)]
|
||||||
|
pub struct PoemData {
|
||||||
|
pub title: Option<String>,
|
||||||
|
pub content: Option<String>,
|
||||||
|
pub creation_date: Option<String>,
|
||||||
|
pub slug: Option<String>,
|
||||||
|
pub dark_mode: Option<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Props)]
|
||||||
|
pub struct PoemChildren<'a> {
|
||||||
|
pub children: Element<'a>,
|
||||||
|
}
|
91
void-fe/src/utils/user_prefs.rs
Normal file
91
void-fe/src/utils/user_prefs.rs
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
pub struct UserPrefs {
|
||||||
|
theme: ThemePref,
|
||||||
|
font: FontPref,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum ThemePref {
|
||||||
|
Light,
|
||||||
|
Dark,
|
||||||
|
Auto,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum FontPref {
|
||||||
|
NerdFont,
|
||||||
|
OpenDyslexic,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UserPrefs {
|
||||||
|
pub fn new() -> UserPrefs {
|
||||||
|
UserPrefs {
|
||||||
|
theme: ThemePref::Auto,
|
||||||
|
font: FontPref::OpenDyslexic,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_theme(&self, is_button: bool) -> String {
|
||||||
|
match &self.theme {
|
||||||
|
ThemePref::Light => Self::light_theme_classes(is_button),
|
||||||
|
ThemePref::Dark => Self::dark_theme_classes(is_button),
|
||||||
|
ThemePref::Auto => Self::auto_theme_classes(is_button),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_theme(mut self, theme: ThemePref) {
|
||||||
|
self.theme = theme;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_font(&self) -> String {
|
||||||
|
match &self.font {
|
||||||
|
FontPref::OpenDyslexic => "...".to_string(),
|
||||||
|
FontPref::NerdFont => "...".to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_font(mut self, font: FontPref) {
|
||||||
|
self.font = font;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_prefs(&self, is_button: bool) -> (String, String) {
|
||||||
|
let theme = self.get_theme(is_button).clone();
|
||||||
|
let font = self.get_font().clone();
|
||||||
|
(theme, font)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_prefs(mut self, prefs: UserPrefs) {
|
||||||
|
self.theme = prefs.theme;
|
||||||
|
self.font = prefs.font;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn light_theme_classes(is_button: bool) -> String {
|
||||||
|
let base_classes = "bg-alice-werefox-grey-lightest ring-alice-werefox-red-dark text-alice-werefox-grey-dark"
|
||||||
|
.to_string();
|
||||||
|
let button_classes = "hover:text-alice-werefox-blue-dark hover:ring-alice-werefox-blue hover:animate-yip transition".to_string();
|
||||||
|
if is_button {
|
||||||
|
return format!("{base_classes} {button_classes}");
|
||||||
|
}
|
||||||
|
base_classes
|
||||||
|
}
|
||||||
|
fn dark_theme_classes(is_button: bool) -> String {
|
||||||
|
let base_classes =
|
||||||
|
"bg-alice-werefox-grey-dark ring-alice-werefox-red text-alice-werefox-grey-light"
|
||||||
|
.to_string();
|
||||||
|
let button_classes = "hover:text-alice-werefox-blue-light hover:ring-alice-werefox-blue hover:animate-yip transition".to_string();
|
||||||
|
if is_button {
|
||||||
|
return format!("{base_classes} {button_classes}");
|
||||||
|
}
|
||||||
|
base_classes
|
||||||
|
}
|
||||||
|
fn auto_theme_classes(is_button: bool) -> String {
|
||||||
|
format!(
|
||||||
|
"{}{}",
|
||||||
|
Self::light_theme_classes(is_button)
|
||||||
|
.split(" ")
|
||||||
|
.map(|c| if c == "transition" { "" } else { c })
|
||||||
|
.collect::<String>(),
|
||||||
|
Self::dark_theme_classes(is_button)
|
||||||
|
.split(" ")
|
||||||
|
.map(|c| format!(" dark:{c}"))
|
||||||
|
.collect::<String>()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -105,6 +105,5 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
darkMode: 'class',
|
|
||||||
plugins: [],
|
plugins: [],
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user