Implemented user settings feature at a basic level, need to fix CSS for components.

This commit is contained in:
Ada Werefox 2023-04-16 21:46:10 -05:00
parent 9d46693ba8
commit b78e2c2d57
24 changed files with 402 additions and 185 deletions

6
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,6 @@
{
"rust-analyzer.linkedProjects": [
"./void-fe/Cargo.toml",
"./void-fe/Cargo.toml"
]
}

Binary file not shown.

File diff suppressed because one or more lines are too long

76
rustfmt.toml Normal file
View File

@ -0,0 +1,76 @@
max_width = 100
hard_tabs = false
tab_spaces = 4
newline_style = "Auto"
indent_style = "Block"
use_small_heuristics = "Default"
fn_call_width = 60
attr_fn_like_width = 70
struct_lit_width = 18
struct_variant_width = 35
array_width = 60
chain_width = 60
single_line_if_else_max_width = 50
wrap_comments = false
format_code_in_doc_comments = false
doc_comment_code_block_width = 100
comment_width = 80
normalize_comments = false
normalize_doc_attributes = false
format_strings = false
format_macro_matchers = false
format_macro_bodies = true
hex_literal_case = "Preserve"
empty_item_single_line = true
struct_lit_single_line = true
fn_single_line = false
where_single_line = false
imports_indent = "Block"
imports_layout = "Mixed"
imports_granularity = "Preserve"
group_imports = "Preserve"
reorder_imports = true
reorder_modules = true
reorder_impl_items = false
type_punctuation_density = "Wide"
space_before_colon = false
space_after_colon = true
spaces_around_ranges = false
binop_separator = "Front"
remove_nested_parens = true
combine_control_expr = true
short_array_element_width_threshold = 10
overflow_delimited_expr = false
struct_field_align_threshold = 0
enum_discrim_align_threshold = 0
match_arm_blocks = true
match_arm_leading_pipes = "Never"
force_multiline_blocks = false
# fn_params_layout = "Tall"
brace_style = "SameLineWhere"
control_brace_style = "AlwaysSameLine"
trailing_semicolon = true
trailing_comma = "Vertical"
match_block_trailing_comma = false
blank_lines_upper_bound = 1
blank_lines_lower_bound = 0
edition = "2021"
version = "One"
inline_attribute_width = 0
format_generated_files = true
merge_derives = true
use_try_shorthand = false
use_field_init_shorthand = false
force_explicit_abi = true
condense_wildcard_suffixes = false
color = "Auto"
# required_version = "1.5.1"
unstable_features = true
disable_all_formatting = false
skip_children = false
hide_parse_errors = false
error_on_line_overflow = false
error_on_unformatted = false
ignore = []
emit_mode = "Files"
make_backup = false

View File

@ -4,4 +4,4 @@ use void_be::web_app_backend;
async fn main() -> Result<(), rocket::Error> { async fn main() -> Result<(), rocket::Error> {
let _rocket = web_app_backend::build_rocket().await.launch().await; let _rocket = web_app_backend::build_rocket().await.launch().await;
Ok(()) Ok(())
} }

View File

@ -13,30 +13,68 @@ 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::utils::prop_structs::PoemRequest;
use void_fe::void_app::{self, VirtualDom}; use void_fe::void_app::{self, VirtualDom};
use void_fe::utils::prop_structs::{DarkModeProps, PoemRequest};
use void_fe::utils::user_prefs::*;
async fn get_user_prefs(cookies: &CookieJar<'_>) -> UserPrefs {
let user_theme = match cookies.get("theme") {
Some(c) => match c.value() {
"auto" => ThemePref::Auto,
"light" => ThemePref::Light,
"dark" => ThemePref::Dark,
_ => {
cookies.remove(Cookie::named("theme"));
cookies.add(Cookie::new("theme", "auto"));
ThemePref::Auto
}
},
None => {
cookies.add(Cookie::new("theme", "auto"));
ThemePref::Auto
}
};
let user_font = match cookies.get("font") {
Some(c) => match c.value() {
"nerd" => FontPref::NerdFont,
"open" => FontPref::OpenDyslexic,
_ => {
cookies.remove(Cookie::named("font"));
cookies.add(Cookie::new("font", "open"));
FontPref::OpenDyslexic
}
},
None => {
cookies.add(Cookie::new("font", "open"));
FontPref::OpenDyslexic
}
};
UserPrefs::new(user_theme, user_font)
}
async fn set_user_theme(cookies: &CookieJar<'_>, theme: &str) {
if theme == "light" || theme == "dark" || theme == "auto" {
cookies.remove(Cookie::named("theme"));
cookies.add(Cookie::new("theme", format!("{theme}")));
} else {
return;
}
}
async fn set_user_font(cookies: &CookieJar<'_>, font: &str) {
if font == "nerd" || font == "open" {
cookies.remove(Cookie::named("font"));
cookies.add(Cookie::new("font", format!("{font}")));
} else {
return;
}
}
#[get("/")] #[get("/")]
async fn index(cookies: &CookieJar<'_>) -> Template { async fn index(cookies: &CookieJar<'_>) -> Template {
let dark_mode = match cookies.get("dark-mode") { let user_prefs = get_user_prefs(cookies).await;
Some(c) => { let mut vdom = VirtualDom::new_with_props(void_app::HomePage, user_prefs);
if c.value() == "true" {
true
} else if c.value() == "false" {
false
} else {
false
}
}
None => false,
};
let mut vdom = VirtualDom::new_with_props(
void_app::HomePage,
DarkModeProps {
slug: Some("/".to_string()),
dark_mode,
},
);
let _ = vdom.rebuild(); let _ = vdom.rebuild();
let output = dioxus_ssr::render(&vdom); let output = dioxus_ssr::render(&vdom);
Template::render( Template::render(
@ -45,45 +83,44 @@ pub mod web_app_backend {
app_title: "A Letter to the Void", app_title: "A Letter to the Void",
style_include: "<link href=/styles/tailwind.min.css rel=stylesheet />", style_include: "<link href=/styles/tailwind.min.css rel=stylesheet />",
test: &output, test: &output,
dark_mode: match dark_mode { dark_mode: "",
true => "dark",
false => ""
},
}, },
) )
} }
#[get("/?dark_mode&<callback>")] #[get("/", rank = 3)]
async fn dark_mode(cookies: &CookieJar<'_>, callback: &str) -> Redirect { async fn settings(cookies: &CookieJar<'_>) -> Template {
match cookies.get("dark-mode") { let user_prefs = get_user_prefs(cookies).await;
Some(_) => cookies.remove(Cookie::named("dark-mode")), let mut vdom = VirtualDom::new_with_props(void_app::SettingsPage, user_prefs);
None => cookies.add(Cookie::new("dark-mode", "true")), let _ = vdom.rebuild();
}; let output = dioxus_ssr::render(&vdom);
let callback_uri = format!("{callback}"); Template::render(
Redirect::to(callback_uri) "index",
context! {
app_title: "Settings",
style_include: "<link href=/styles/tailwind.min.css rel=stylesheet />",
test: &output,
dark_mode: ""
},
)
}
#[get("/?<theme>", rank = 2)]
async fn theme(cookies: &CookieJar<'_>, theme: &str) -> Redirect {
set_user_theme(cookies, theme).await;
Redirect::to("/settings")
}
#[get("/?<font>")]
async fn font(cookies: &CookieJar<'_>, font: &str) -> Redirect {
set_user_font(cookies, font).await;
Redirect::to("/settings")
} }
#[get("/")] #[get("/")]
async fn poem_list(cookies: &CookieJar<'_>) -> Template { async fn poem_list(cookies: &CookieJar<'_>) -> Template {
let dark_mode = match cookies.get("dark-mode") { let user_prefs = get_user_prefs(cookies).await;
Some(c) => { let mut vdom = VirtualDom::new_with_props(void_app::PoemListPage, user_prefs);
if c.value() == "true" {
true
} else if c.value() == "false" {
false
} else {
false
}
}
None => false,
};
let mut vdom = VirtualDom::new_with_props(
void_app::PoemListPage,
DarkModeProps {
slug: Some(String::from("/poems")),
dark_mode,
},
);
let _ = vdom.rebuild(); let _ = vdom.rebuild();
let output = dioxus_ssr::render(&vdom); let output = dioxus_ssr::render(&vdom);
Template::render( Template::render(
@ -92,33 +129,19 @@ pub mod web_app_backend {
app_title: "A Letter to the Void", app_title: "A Letter to the Void",
style_include: "<link href=/styles/tailwind.min.css rel=stylesheet />", style_include: "<link href=/styles/tailwind.min.css rel=stylesheet />",
test: &output, test: &output,
dark_mode: match dark_mode { dark_mode: "",
true => "dark",
false => ""
},
}, },
) )
} }
#[get("/<entry>")] #[get("/<entry>")]
async fn poem(cookies: &CookieJar<'_>, entry: &str) -> Template { async fn poem(cookies: &CookieJar<'_>, entry: &str) -> Template {
let dark_mode = match cookies.get("dark-mode") { let user_prefs = get_user_prefs(cookies).await;
Some(c) => {
if c.value() == "true" {
true
} else if c.value() == "false" {
false
} else {
false
}
}
None => false,
};
let mut vdom = VirtualDom::new_with_props( let mut vdom = VirtualDom::new_with_props(
void_app::PoemPage, void_app::PoemPage,
PoemRequest { PoemRequest {
slug: format!("{entry}"), slug: format!("{entry}"),
dark_mode: Some(dark_mode), user_prefs,
}, },
); );
let _ = vdom.rebuild(); let _ = vdom.rebuild();
@ -129,10 +152,7 @@ pub mod web_app_backend {
app_title: "A Letter to the Void", app_title: "A Letter to the Void",
style_include: "<link href=/styles/tailwind.min.css rel=stylesheet />", style_include: "<link href=/styles/tailwind.min.css rel=stylesheet />",
test: &output, test: &output,
dark_mode: match dark_mode { dark_mode: "",
true => "dark",
false => ""
},
}, },
) )
} }
@ -144,7 +164,8 @@ pub mod web_app_backend {
.mount("/styles", FileServer::from("public/styles")) .mount("/styles", FileServer::from("public/styles"))
.mount("/fonts", FileServer::from("public/fonts")) .mount("/fonts", FileServer::from("public/fonts"))
.mount("/poems", routes![poem_list, poem]) .mount("/poems", routes![poem_list, poem])
.mount("/", routes![dark_mode, index]) .mount("/settings", routes![settings, theme, font])
.mount("/", routes![index])
.attach(Template::fairing()) .attach(Template::fairing())
} }
} }

View File

@ -20,23 +20,29 @@ title = "A Letter to the Void"
[web.watcher] [web.watcher]
# when watcher trigger, regenerate the `index.html` # when watcher triggers, regenerate the `index.html`
reload_html = true reload_html = true
# which files or dirs will be watcher monitoring # which files or dirs will be watcher monitoring
watch_path = ["src", "data", "../public", "tailwind.config.js", "Dioxus.toml", "Cargo.toml", "build.rs"] watch_path = ["src", "data", "../public", "tailwind.config.js", "Dioxus.toml", "Cargo.toml", "build.rs"]
# implement redirect on 404
index_on_404 = true
# include `assets` in web platform # include `assets` in web platform
[web.resource] [web.resource]
# CSS style file # CSS style file
style = ["styles/tailwind.min.css"] style = ["/styles/tailwind.min.css"]
# Javascript code file # Javascript code file
script = [] script = []
[web.resource.dev] [web.resource.dev]
# CSS style file
style = ["/styles/tailwind.min.css"]
# Javascript code file # Javascript code file
# serve: [dev-server] only # serve: [dev-server] only
script = [] script = []

View File

@ -17,4 +17,4 @@ fn main() {
// for f in std::fs::read_dir("../data/poems").unwrap() { // for f in std::fs::read_dir("../data/poems").unwrap() {
// content.push(std::fs::read_to_string(f.unwrap().path()).unwrap()); // content.push(std::fs::read_to_string(f.unwrap().path()).unwrap());
// } // }
// } // }

View File

@ -0,0 +1,7 @@
Welcome, and I hope you enjoy your stay!\
"A Letter to the Void" is a passion project of mine in which I wrote poems about my past life experiences, present, and hopes for the future throughout my transition.\
The topics range from my feelings through transitioning (of course), past abuse, mental health exploration, and an overall journey to grow and become a better creature.\
I hope you enjoy the time you spend here, and sincerely, thank you.\
\
🖤 Alice Icehart Werefox

View File

@ -1,4 +1,6 @@
pub mod void_poem; pub mod void_buttons;
pub mod void_footer; pub mod void_footer;
pub mod void_page;
pub mod void_poem;
pub mod void_title; pub mod void_title;
pub mod void_buttons; pub mod void_content;

View File

@ -1,5 +1,5 @@
use crate::utils::prop_structs::{ButtonProps, ContentChildren};
use dioxus::prelude::*; use dioxus::prelude::*;
use crate::utils::prop_structs::ButtonProps;
#[cfg(target_family = "wasm")] #[cfg(target_family = "wasm")]
use dioxus_router::Link; use dioxus_router::Link;
@ -7,8 +7,7 @@ use dioxus_router::Link;
pub fn BackToHomePage(cx: Scope) -> Element { pub fn BackToHomePage(cx: Scope) -> Element {
#[cfg(any(target_family = "windows", target_family = "unix"))] #[cfg(any(target_family = "windows", target_family = "unix"))]
return cx.render(rsx!{ 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", a {href: "/",
href: "/",
p { p {
"Back to the homepage" "Back to the homepage"
} }
@ -16,8 +15,7 @@ pub fn BackToHomePage(cx: Scope) -> Element {
}); });
#[cfg(target_family = "wasm")] #[cfg(target_family = "wasm")]
return cx.render(rsx!{ 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", Link { to: "/",
to: "/",
p { p {
"Back to the homepage" "Back to the homepage"
} }
@ -32,18 +30,24 @@ pub fn NavigationButton(cx: Scope<ButtonProps>) -> Element {
let slug_ref = slug.as_str(); let slug_ref = slug.as_str();
#[cfg(any(target_family = "windows", target_family = "unix"))] #[cfg(any(target_family = "windows", target_family = "unix"))]
return cx.render(rsx!{ 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", a { href: "{slug_ref}",
href: "{slug_ref}",
"{title_ref}" "{title_ref}"
} }
}); });
#[cfg(target_family = "wasm")] #[cfg(target_family = "wasm")]
return cx.render(rsx!{ 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", Link { to: "{slug_ref}",
to: "{slug_ref}",
div { div {
dangerous_inner_html: "{title_ref}", dangerous_inner_html: "{title_ref}",
} }
} }
}); });
}
pub fn ButtonGroup<'a>(cx: Scope<'a, ContentChildren<'a>>) -> Element {
cx.render(rsx! {
div { class: "grid md:grid-flow-col grid-flow-row gap-y-4",
&cx.props.children
}
})
} }

View File

@ -0,0 +1,19 @@
// Might wanna move stuff form `void_poem.rs` into here...
use crate::utils::prop_structs::{ContentProps};
use dioxus::prelude::*;
pub fn RenderContent(cx: Scope<ContentProps>) -> Element {
let content = &cx.props.content;
#[cfg(any(target_family = "windows", target_family = "unix"))]
return cx.render(rsx!{
div { class: "flex p-4 ml-2 mr-2 ring-4",
"{content}",
}
});
#[cfg(target_family = "wasm")]
return cx.render(rsx!{
div { class: "flex p-4 ml-2 mr-2 ring-4",
dangerous_inner_html: "{content}",
}
});
}

View File

@ -1,15 +1,19 @@
use dioxus::prelude::*; use dioxus::prelude::*;
use crate::components::void_buttons::NavigationButton;
pub fn Footer(cx: Scope) -> Element { pub fn Footer(cx: Scope) -> Element {
cx.render(rsx!{ cx.render(rsx! {
MutantStandardFooter {} MutantStandardFooter {}
}) })
} }
fn MutantStandardFooter(cx: Scope) -> Element { fn MutantStandardFooter(cx: Scope) -> Element {
cx.render(rsx!{ 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", div {
"This site uses Mutant Standard emoji, which are licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License" NavigationButton { title: "⚙️".to_string(), slug: "/settings".to_string() }
div {
"This site uses Mutant Standard emoji, which are licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License"
}
} }
}) })
} }

View File

@ -0,0 +1,13 @@
use crate::utils::prop_structs::PageChildren;
use dioxus::prelude::*;
pub fn PageBase<'a>(cx: Scope<'a, PageChildren<'a>>) -> Element {
cx.render(rsx!{
// div { class: "bg-alice-werefox-grey-lightest ring-alice-werefox-red-dark text-alice-werefox-grey-dark dark:bg-alice-werefox-grey-dark dark:ring-alice-werefox-red dark:text-alice-werefox-grey-light let button_classes hover:text-alice-werefox-blue-dark hover:ring-alice-werefox-blue dark:hover:text-alice-werefox-blue-light dark:hover:ring-alice-werefox-blue hover:animate-yip transition", hidden: true }
div {
div { class: "container space-y-4 mx-auto p-4",
&cx.props.children
}
}
})
}

View File

@ -1,8 +1,9 @@
use dioxus::prelude::*;
use crate::utils::helpers;
use crate::utils::prop_structs::{PoemChildren, PoemData};
use crate::components::void_buttons::*; use crate::components::void_buttons::*;
use crate::components::void_title::*; use crate::components::void_title::*;
use crate::utils::helpers;
use crate::utils::prop_structs::{PoemChildren, PoemData};
use super::super::utils::user_prefs::*;
use dioxus::prelude::*;
pub fn PoemList(cx: Scope) -> Element { pub fn PoemList(cx: Scope) -> Element {
let poem_list = helpers::get_poem_list(); let poem_list = helpers::get_poem_list();
@ -30,7 +31,7 @@ pub fn GetPoem(cx: Scope<PoemData>) -> Element {
let slug = String::from(cx.props.slug.clone().expect("No slug specified.")); let slug = String::from(cx.props.slug.clone().expect("No slug specified."));
let (title, content, creation_date) = helpers::get_poem(slug.clone()); let (title, content, creation_date) = helpers::get_poem(slug.clone());
cx.render(rsx! { cx.render(rsx! {
Title { title: title, is_html: true } Title { title: title, is_html: true, user_prefs: UserPrefs::new(ThemePref::Auto, FontPref::OpenDyslexic)}
MakePoem{ MakePoem{
PoemContent { content: content, creation_date: creation_date } PoemContent { content: content, creation_date: creation_date }
} }
@ -47,8 +48,8 @@ pub fn PoemContent(cx: Scope<PoemData>) -> Element {
#[cfg(any(target_family = "unix", target_family = "windows"))] #[cfg(any(target_family = "unix", target_family = "windows"))]
return cx.render(rsx! { return cx.render(rsx! {
div { class: "flex p-2 mx-auto max-w-full justify-center", 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", details { class: "group p-4 max-w-fit space-y-4",
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", summary { class: "group-open:before:content-['Close'] before:content-['Open'] flex justify-center p-2 ring-2",
} }
div { class: "font-nerd flex flex-col space-y-4 py-4", "{content}{creation_date}" div { class: "font-nerd flex flex-col space-y-4 py-4", "{content}{creation_date}"
} }
@ -58,8 +59,8 @@ pub fn PoemContent(cx: Scope<PoemData>) -> Element {
#[cfg(target_family = "wasm")] #[cfg(target_family = "wasm")]
return cx.render(rsx! { return cx.render(rsx! {
div { class: "flex p-2 mx-auto max-w-full justify-center", 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", details { class: "group p-4 max-w-fit space-y-4",
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", summary { class: "group-open:before:content-['Close'] before:content-['Open'] flex justify-center p-2 ring-2",
} }
div { class: "font-nerd flex flex-col space-y-4 py-4", div { class: "font-nerd flex flex-col space-y-4 py-4",
dangerous_inner_html: "{content}{creation_date}", dangerous_inner_html: "{content}{creation_date}",
@ -67,4 +68,4 @@ pub fn PoemContent(cx: Scope<PoemData>) -> Element {
} }
} }
}); });
} }

View File

@ -1,13 +1,14 @@
use dioxus::prelude::*;
use crate::utils::prop_structs::TitleProps; use crate::utils::prop_structs::TitleProps;
use dioxus::prelude::*;
pub fn Title(cx: Scope<TitleProps>) -> Element { pub fn Title(cx: Scope<TitleProps>) -> Element {
let user_prefs = cx.props.user_prefs.clone();
let title = cx.props.title.clone(); let title = cx.props.title.clone();
let is_html = cx.props.is_html; let is_html = cx.props.is_html;
cx.render(rsx!{ 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", div { class: "p-4 ring-4",
span { class: "flex flex-row mx-auto max-w-full justify-center text-xl text-center", span { class: "flex flex-row mx-auto max-w-full justify-center text-xl text-center",
TitleHtml { title: title, is_html: is_html } TitleHtml { title: title, is_html: is_html, user_prefs: user_prefs }
} }
} }
}) })
@ -17,22 +18,22 @@ fn TitleHtml(cx: Scope<TitleProps>) -> Element {
let title = cx.props.title.clone(); let title = cx.props.title.clone();
if cx.props.is_html { if cx.props.is_html {
#[cfg(any(target_family = "unix", target_family = "windows"))] #[cfg(any(target_family = "unix", target_family = "windows"))]
return cx.render(rsx!{ return cx.render(rsx! {
span { class: "flex flex-row align-middle mx-auto max-w-full justify-center", span { class: "flex flex-row align-middle mx-auto max-w-full justify-center",
"{title}&nbsp;" "{title}&nbsp;"
} }
}); });
#[cfg(target_family = "wasm")] #[cfg(target_family = "wasm")]
return cx.render(rsx!{ return cx.render(rsx! {
span { class: "flex flex-row align-middle mx-auto max-w-full justify-center", span { class: "flex flex-row align-middle mx-auto max-w-full justify-center",
div { dangerous_inner_html: "{title}", } div { dangerous_inner_html: "{title}", }
} }
}); });
} else { } else {
return cx.render(rsx!{ return cx.render(rsx! {
span { span {
"{title}" "{title}"
} }
}) });
} }
} }

View File

@ -6,3 +6,8 @@
font-family: "OpenDyslexic"; font-family: "OpenDyslexic";
src: url("/fonts/OpenDyslexic-Regular.otf"); src: url("/fonts/OpenDyslexic-Regular.otf");
} }
@font-face {
font-family: "DejaVuSansMono";
src: url("/fonts/DejaVuSansMono.ttf");
}

View File

@ -4,50 +4,50 @@
#![allow(non_snake_case)] #![allow(non_snake_case)]
mod components; mod components;
pub mod utils; 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 rust_embed::RustEmbed;
use crate::components::void_buttons::*; use crate::components::void_buttons::*;
use crate::components::void_content::*;
use crate::components::void_footer::*; use crate::components::void_footer::*;
use crate::components::void_page::PageBase;
use crate::components::void_poem::*; use crate::components::void_poem::*;
use crate::components::void_title::*; use crate::components::void_title::*;
use crate::utils::helpers; use crate::utils::helpers;
use crate::utils::prop_structs::*; use crate::utils::prop_structs::*;
use crate::utils::user_prefs::*;
#[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, Route, Router, Redirect}; use dioxus_router::{Link, Redirect, Route, Router};
#[cfg(any(target_family = "wasm"))] #[cfg(any(target_family = "wasm"))]
use dioxus_use_storage::use_local_storage; use dioxus_use_storage::use_local_storage;
#[derive(RustEmbed)]
#[folder = "data/poems"]
pub struct Poems;
#[cfg(target_family = "wasm")] #[cfg(target_family = "wasm")]
pub fn DioxusApp(cx: Scope) -> Element { pub fn DioxusApp(cx: Scope) -> Element {
// use dioxus_router::Redirect; // use dioxus_router::Redirect;
let user_prefs = UserPrefs::new(ThemePref::Auto, FontPref::OpenDyslexic);
cx.render(rsx! { cx.render(rsx! {
div { class: "bg-alice-werefox-grey-lightest ring-alice-werefox-red-dark text-alice-werefox-grey-dark dark:bg-alice-werefox-grey-dark dark:ring-alice-werefox-red dark:text-alice-werefox-grey-light let button_classes hover:text-alice-werefox-blue-dark hover:ring-alice-werefox-blue dark:hover:text-alice-werefox-blue-light dark:hover:ring-alice-werefox-blue hover:animate-yip transition" }
Router { Router {
Route { to: "/", self::HomePage { dark_mode: true, } } Route { to: "/",
Route { to: "/poems", self::HomePage { user_prefs }
PoemListPage { slug: "".to_string(), dark_mode: true, }
}
Route { to: "/poems/:slug",
PoemPage { slug: "".to_string(), dark_mode: true, }
} }
Route { to: "/poems", PoemListPage { user_prefs } }
Route { to: "/poems/:slug", PoemPage { slug: "".to_string(), user_prefs } }
Route { to: "/settings", SettingsPage { user_prefs } }
Route { to: "/settings/dark", SettingsPage { user_prefs } }
Route { to: "/settings/font", SettingsPage { user_prefs } }
Route { to: "", PageNotFound {} } Route { to: "", PageNotFound {} }
} }
}) })
@ -61,35 +61,22 @@ pub mod void_app {
}) })
} }
/// 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<UserPrefs>) -> Element {
#[cfg(any(target_family = "unix", target_family = "windows"))] let user_prefs = cx.props.clone();
let slug = cx.props.slug.clone().expect("Slug for dark mode redirect."); let (user_theme, user_font) = user_prefs.get_prefs(false);
#[cfg(target_family = "wasm")]
let slug = "".to_string();
let title = "A Letter to the Void".to_string(); let title = "A Letter to the Void".to_string();
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: "bg-alice-werefox-grey-lightest ring-alice-werefox-red-dark text-alice-werefox-grey-dark dark:bg-alice-werefox-grey-dark dark:ring-alice-werefox-red dark:text-alice-werefox-grey-light let button_classes hover:text-alice-werefox-blue-dark hover:ring-alice-werefox-blue dark:hover:text-alice-werefox-blue-light dark:hover:ring-alice-werefox-blue hover:animate-yip transition", hidden: true }
div { class: "container space-y-4 mx-auto p-4", div { class: "{user_theme} {user_font}",
Title { title: title, is_html: false } PageBase {
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", Title { title: title, is_html: false, user_prefs: user_prefs }
p { class: "text-lg text-center", RenderContent { content: helpers::get_homepage_paragraph() }
"Welcome, and I hope you enjoy your stay!" ButtonGroup {
br {} NavigationButton { title: "See Latest Entry".to_string(), slug: helpers::get_latest_entry("".to_string()) }
"\"A Letter to the Void\" is a passion project of mine in which I wrote poems about my past life experiences, present, and hopes for the future throughout my transition." NavigationButton { title: "See Oldest Entry".to_string(), slug: helpers::get_oldest_entry("".to_string()) }
br {} NavigationButton { title: "See All Entries".to_string(), slug: "/poems".to_string() }
"The topics range from my feelings through transitioning (of course), past abuse, mental health exploration, and an overall journey to grow and become a better creature."
br {}
"I hope you enjoy the time you spend here, and sincerely, thank you."
br {}
br {}
"🖤 Alice Icehart Werefox"
}
} }
NavigationButton { title: "See Latest Entry".to_string(), slug: helpers::get_latest_entry(slug.clone()) }
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() }
Footer {} Footer {}
} }
} }
@ -97,11 +84,13 @@ pub mod void_app {
} }
/// 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<UserPrefs>) -> Element {
cx.render(rsx!{ let user_prefs = cx.props.clone();
div { class: "min-h-screen font-nerd bg-alice-werefox-grey-light dark:bg-alice-werefox-grey", let (user_theme, user_font) = user_prefs.get_prefs(false);
div { class: "container space-y-4 mx-auto p-4", cx.render(rsx! {
Title { title: "A Letter to the Void".to_string(), is_html: false } div { class: "{user_theme} {user_font}",
PageBase {
Title { title: "A Letter to the Void".to_string(), is_html: false, user_prefs: user_prefs }
BackToHomePage {} BackToHomePage {}
PoemList {} PoemList {}
BackToHomePage {} BackToHomePage {}
@ -112,6 +101,8 @@ pub mod void_app {
} }
pub fn PoemPage(cx: Scope<PoemRequest>) -> Element { pub fn PoemPage(cx: Scope<PoemRequest>) -> Element {
let user_prefs = cx.props.user_prefs.clone();
let (user_theme, user_font) = user_prefs.get_prefs(false);
#[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();
#[cfg(target_family = "wasm")] #[cfg(target_family = "wasm")]
@ -120,17 +111,12 @@ pub mod void_app {
.segment("slug") .segment("slug")
.expect("No slug specified."), .expect("No slug specified."),
); );
let dark_mode = cx
.props
.dark_mode
.clone()
.expect("Dark mode prop not passed.");
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: "{user_theme} {user_font}",
div { class: "container space-y-4 mx-auto p-4", PageBase {
BackToHomePage {} BackToHomePage {}
GetPoem { slug: slug.clone(), dark_mode: dark_mode } GetPoem { slug: slug.clone() }
div { class: "grid md:grid-cols-4 md:grid-rows-1 grid-cols-1 grid-rows-4 gap-y-4", ButtonGroup {
NavigationButton { title: "Oldest".to_string(), slug: helpers::get_oldest_entry(slug.clone()) } NavigationButton { title: "Oldest".to_string(), slug: helpers::get_oldest_entry(slug.clone()) }
NavigationButton { title: "Previous".to_string(), slug: helpers::get_previous_entry(slug.clone()) } NavigationButton { title: "Previous".to_string(), slug: helpers::get_previous_entry(slug.clone()) }
NavigationButton { title: "Next".to_string(), slug: helpers::get_next_entry(slug.clone()) } NavigationButton { title: "Next".to_string(), slug: helpers::get_next_entry(slug.clone()) }
@ -142,4 +128,33 @@ pub mod void_app {
} }
}) })
} }
pub fn SettingsPage(cx: Scope<UserPrefs>) -> Element {
let user_prefs = cx.props.clone();
let (user_theme, user_font) = user_prefs.get_prefs(false);
cx.render(rsx! {
div { class: "{user_theme} {user_font}",
PageBase {
Title { title: "Settings".to_string(), is_html: false, user_prefs: user_prefs }
BackToHomePage {}
div { class: "grid grid-flow-row space-y-4",
ButtonGroup {
NavigationButton { title: "Light".to_string(), slug: "/settings/?theme=light".to_string() }
NavigationButton { title: "Dark".to_string(), slug: "/settings/?theme=dark".to_string() }
NavigationButton { title: "Auto".to_string(), slug: "/settings/?theme=auto".to_string() }
}
ButtonGroup {
span { class: "font-nerd",
NavigationButton { title: "Nerd Font".to_string(), slug: "/settings/?font=nerd".to_string() }
}
span { class: "font-open",
NavigationButton { title: "Open Dyslexic".to_string(), slug: "/settings/?font=open".to_string() }
}
}
}
BackToHomePage {}
}
}
})
}
} }

View File

@ -1,4 +1,3 @@
#[cfg(target_family = "wasm")] #[cfg(target_family = "wasm")]
use console_error_panic_hook; use console_error_panic_hook;

View File

@ -1,3 +1,3 @@
pub mod helpers; pub mod helpers;
pub mod prop_structs; pub mod prop_structs;
pub mod user_prefs; pub mod user_prefs;

View File

@ -1,6 +1,21 @@
use std::collections::VecDeque; use rust_embed::RustEmbed;
use crate::void_app::Poems;
use markdown::Options; use markdown::Options;
use std::collections::VecDeque;
#[derive(RustEmbed)]
#[folder = "data/other"]
struct OtherData;
#[derive(RustEmbed)]
#[folder = "data/poems"]
struct Poems;
pub fn get_homepage_paragraph() -> String {
let homepage_paragraph_content = OtherData::get("homepage.md").expect("Found homepage paragraph.");
let homepage_paragraph_to_string = std::str::from_utf8(homepage_paragraph_content.data.as_ref()).expect("Homepage file is valid UTF-8");
let test = markdown::to_html_with_options(homepage_paragraph_to_string, &Options::gfm()).unwrap();
test
}
pub fn get_poem(slug: String) -> (String, String, String) { pub fn get_poem(slug: String) -> (String, String, String) {
let filename = String::from(String::from(slug.clone()) + ".md"); let filename = String::from(String::from(slug.clone()) + ".md");
@ -16,7 +31,11 @@ pub fn get_poem(slug: String) -> (String, String, String) {
markdown::to_html_with_options(poem_title, &Options::gfm()).unwrap(); markdown::to_html_with_options(poem_title, &Options::gfm()).unwrap();
let poem_content_to_html_string = let poem_content_to_html_string =
markdown::to_html_with_options(poem_content.as_str(), &Options::gfm()).unwrap(); markdown::to_html_with_options(poem_content.as_str(), &Options::gfm()).unwrap();
(poem_title_to_html_string, poem_content_to_html_string, creation_date) (
poem_title_to_html_string,
poem_content_to_html_string,
creation_date,
)
} }
pub fn get_poem_list() -> Vec<(String, String)> { pub fn get_poem_list() -> Vec<(String, String)> {
@ -94,4 +113,4 @@ pub fn get_next_entry(current: String) -> String {
Some(entry) => format!("/poems/{entry}"), Some(entry) => format!("/poems/{entry}"),
None => format!("/poems/{current}"), None => format!("/poems/{current}"),
} }
} }

View File

@ -1,21 +1,23 @@
use crate::void_app::{Element, Props}; use crate::void_app::{Element, Props};
use super::user_prefs::UserPrefs;
#[derive(PartialEq, Props)] #[derive(PartialEq, Props)]
pub struct PoemRequest { pub struct PoemRequest {
pub slug: String, pub slug: String,
pub dark_mode: Option<bool>, pub user_prefs: UserPrefs,
}
#[derive(PartialEq, Props)]
pub struct DarkModeProps {
pub dark_mode: bool,
pub slug: Option<String>,
} }
#[derive(PartialEq, Props)] #[derive(PartialEq, Props)]
pub struct TitleProps { pub struct TitleProps {
pub title: String, pub title: String,
pub is_html: bool, pub is_html: bool,
pub user_prefs: UserPrefs,
}
#[derive(PartialEq, Props)]
pub struct ContentProps {
pub content: String,
} }
#[derive(PartialEq, Props)] #[derive(PartialEq, Props)]
@ -33,7 +35,18 @@ pub struct PoemData {
pub dark_mode: Option<bool>, pub dark_mode: Option<bool>,
} }
// These next three should all just be one prop.
#[derive(Props)] #[derive(Props)]
pub struct PoemChildren<'a> { pub struct PoemChildren<'a> {
pub children: Element<'a>, pub children: Element<'a>,
} }
#[derive(Props)]
pub struct PageChildren<'a> {
pub children: Element<'a>,
}
#[derive(Props)]
pub struct ContentChildren<'a> {
pub children: Element<'a>,
}

View File

@ -1,24 +1,29 @@
use dioxus::prelude::*;
#[derive(PartialEq, Props, Clone)]
pub struct UserPrefs { pub struct UserPrefs {
theme: ThemePref, theme: ThemePref,
font: FontPref, font: FontPref,
} }
#[derive(PartialEq, Clone)]
pub enum ThemePref { pub enum ThemePref {
Light, Light,
Dark, Dark,
Auto, Auto,
} }
#[derive(PartialEq, Clone)]
pub enum FontPref { pub enum FontPref {
NerdFont, NerdFont,
OpenDyslexic, OpenDyslexic,
} }
impl UserPrefs { impl UserPrefs {
pub fn new() -> UserPrefs { pub fn new(theme: ThemePref, font: FontPref) -> UserPrefs {
UserPrefs { UserPrefs {
theme: ThemePref::Auto, theme,
font: FontPref::OpenDyslexic, font,
} }
} }
@ -36,8 +41,8 @@ impl UserPrefs {
pub fn get_font(&self) -> String { pub fn get_font(&self) -> String {
match &self.font { match &self.font {
FontPref::OpenDyslexic => "...".to_string(), FontPref::OpenDyslexic => "font-open".to_string(),
FontPref::NerdFont => "...".to_string(), FontPref::NerdFont => "font-nerd".to_string(),
} }
} }
@ -77,10 +82,10 @@ impl UserPrefs {
} }
fn auto_theme_classes(is_button: bool) -> String { fn auto_theme_classes(is_button: bool) -> String {
format!( format!(
"{}{}", "{} {}",
Self::light_theme_classes(is_button) Self::light_theme_classes(is_button)
.split(" ") .split(" ")
.map(|c| if c == "transition" { "" } else { c }) .map(|c| if c == "transition" { "".to_string() } else { format!("{c} ") })
.collect::<String>(), .collect::<String>(),
Self::dark_theme_classes(is_button) Self::dark_theme_classes(is_button)
.split(" ") .split(" ")

View File

@ -8,7 +8,8 @@ module.exports = {
theme: { theme: {
extend: { extend: {
fontFamily: { fontFamily: {
nerd: ["OpenDyslexic"], nerd: ["DejaVuSansMono"],
open: ["OpenDyslexic"],
}, },
colors: { colors: {
"ada-werefox-cyan": { "ada-werefox-cyan": {