diff --git a/Dioxus.toml b/Dioxus.toml index f8c8ec9..9973e3b 100644 --- a/Dioxus.toml +++ b/Dioxus.toml @@ -4,7 +4,7 @@ name = "info-werefox-cafe" # Dioxus App Default Platform -# desktop, web, mobile, ssr +# desktop, web, fullstack, mobile default_platform = "fullstack" # `build` & `serve` dist path @@ -24,7 +24,7 @@ title = "About A Werefox" reload_html = true # which files or dirs will be watcher monitoring -watch_path = ["src", "data", "public", "tailwind.config.js", "Dioxus.toml", "Cargo.toml"] +watch_path = ["src", "public", "tailwind.config.js", "Dioxus.toml", "Cargo.toml"] # implement redirect on 404 index_on_404 = true diff --git a/public/tailwind.css b/public/tailwind.css index afe7654..3493814 100644 --- a/public/tailwind.css +++ b/public/tailwind.css @@ -645,6 +645,10 @@ video { display: none; } +.h-4 { + height: 1rem; +} + .h-6 { height: 1.5rem; } @@ -653,16 +657,12 @@ video { height: 2rem; } -.h-4 { - height: 1rem; -} - .min-h-screen { min-height: 100vh; } -.min-h-full { - min-height: 100%; +.w-4 { + width: 1rem; } .w-6 { @@ -677,14 +677,6 @@ video { width: 100%; } -.w-4 { - width: 1rem; -} - -.min-w-full { - min-width: 100%; -} - .max-w-3xl { max-width: 48rem; } @@ -710,18 +702,6 @@ video { flex-basis: 100%; } -.-scale-100 { - --tw-scale-x: -1; - --tw-scale-y: -1; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); -} - -.scale-50 { - --tw-scale-x: .5; - --tw-scale-y: .5; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); -} - .-scale-x-100 { --tw-scale-x: -1; transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); @@ -743,20 +723,6 @@ video { animation: bounce 1s infinite; } -@keyframes wiggle { - 0%, 100% { - transform: rotate(-1deg); - } - - 50% { - transform: rotate(1deg); - } -} - -.animate-wiggle { - animation: wiggle 7s ease-in-out infinite; -} - @keyframes jiggle { 0%, 100% { transform: rotate(0deg); @@ -775,6 +741,20 @@ video { animation: jiggle 5s cubic-bezier(0.75,0.25,0.25,0.75) infinite; } +@keyframes wiggle { + 0%, 100% { + transform: rotate(-1deg); + } + + 50% { + transform: rotate(1deg); + } +} + +.animate-wiggle { + animation: wiggle 7s ease-in-out infinite; +} + .grid-cols-1 { grid-template-columns: repeat(1, minmax(0, 1fr)); } diff --git a/src/components.rs b/src/components.rs index d7b1ad1..bc153ab 100644 --- a/src/components.rs +++ b/src/components.rs @@ -8,4 +8,6 @@ pub mod title_card; pub mod home_page; pub mod page_button; pub mod identity_button; -pub mod project_card; \ No newline at end of file +pub mod project_card; +pub mod back_button; +pub mod project_foldable; \ No newline at end of file diff --git a/src/components/back_button.rs b/src/components/back_button.rs new file mode 100644 index 0000000..30ab328 --- /dev/null +++ b/src/components/back_button.rs @@ -0,0 +1,21 @@ +use crate::{info_app::Route, utils::prop_structs::ImageProps}; +use dioxus::prelude::*; +use dioxus_router::prelude::*; + +#[component] +pub fn BackButton(back_to: Route) -> Element { + rsx! { + nav { + div { + Link { + to: back_to, + class: "p-2 w-full inline-block text-lg text-center rounded-sm ring-2 bg-alice-werefox-grey-lightest dark:bg-alice-werefox-grey-dark ring-alice-werefox-red-dark dark:ring-alice-werefox-red hover:ring-alice-werefox-blue-dark dark:hover:ring-alice-werefox-blue text-alice-werefox-red-dark dark:text-alice-werefox-red-light hover:text-alice-werefox-blue-dark dark:hover:text-alice-werefox-blue-light focus:text-alice-werefox-blue-dark dark:focus:text-alice-werefox-blue-light hover:animate-yip transition", + span { class: "animate-jiggle sm:w-6 sm:h-6 w-4 h-6 inline-block align-top", + img { src: "/emoji/:aliceheartblack:.png", alt: "An emoji of Alice chomping a black heart." } + } + " Take me back!" + } + } + } + } +} diff --git a/src/components/basic_page.rs b/src/components/basic_page.rs index 7671540..2c0d06f 100644 --- a/src/components/basic_page.rs +++ b/src/components/basic_page.rs @@ -1,5 +1,8 @@ use crate::components::footer_card::*; use crate::components::title_card::*; +use crate::components::back_button::*; +use crate::info_app::Route; +use crate::utils::prop_structs::ImageProps; use dioxus::prelude::*; #[component] @@ -14,7 +17,9 @@ pub fn BasicPage(page_title: String, children: Element) -> Element { } div { class: "container space-y-4 mx-auto px-4 py-4", TitleCard { card_title: page_title } + BackButton { back_to: Route::Home {} } {children}, + BackButton { back_to: Route::Home {} } FooterCard {} } } diff --git a/src/components/footer_button.rs b/src/components/footer_button.rs index 4bbc1e2..04d55f3 100644 --- a/src/components/footer_button.rs +++ b/src/components/footer_button.rs @@ -12,4 +12,4 @@ pub fn FooterButton(url: String, inner_text: String) -> Element { } } } -} +} \ No newline at end of file diff --git a/src/components/project_card.rs b/src/components/project_card.rs index 49f79fc..80b5458 100644 --- a/src/components/project_card.rs +++ b/src/components/project_card.rs @@ -4,21 +4,31 @@ use dioxus::prelude::*; #[component] pub fn ProjectCard( project_title: String, - project_description: String, + project_description: Option, project_link: String, - image_props: ImageProps, + image_props: Option, ) -> Element { rsx! { div { a { href: "{project_link}", + target: "_blank", ul { class: "p-4 space-y-2 rounded-sm ring-2 text-alice-werefox-red-dark dark:text-alice-werefox-red-light hover:text-alice-werefox-blue-dark dark:hover:text-alice-werefox-blue-light ring-alice-werefox-red-dark dark:ring-alice-werefox-red hover:ring-alice-werefox-blue-dark dark:hover:ring-alice-werefox-blue hover:animate-yip transition", div { class: "text-lg text-center", + match image_props { + Some(image) => rsx! { span { class: "animate-jiggle sm:w-6 sm:h-6 w-4 h-6 inline-block align-top", - img { src: "{image_props.src}", alt: "{image_props.alt}" } - } + img { src: "{image.src}", alt: "{image.alt}" } + }}, + _ => rsx!{}, + }, " {project_title}" } - div { class: "text-md text-center", "{project_description}" } + match project_description { + Some(description) => rsx! { + div { class: "text-md text-center", "{description}" } + }, + _ => rsx!{}, + } } } } diff --git a/src/components/project_foldable.rs b/src/components/project_foldable.rs new file mode 100644 index 0000000..62c19f5 --- /dev/null +++ b/src/components/project_foldable.rs @@ -0,0 +1,25 @@ +use crate::utils::prop_structs::*; +use dioxus::prelude::*; + +#[component] +pub fn ProjectFoldable( + project_title: String, + project_description: String, + image_props: ImageProps, + children: Element, +) -> Element { + rsx! { + details { class: "p-4 space-y-4 rounded-sm ring-2 text-alice-werefox-red-dark dark:text-alice-werefox-red-light hover:text-alice-werefox-blue-dark dark:hover:text-alice-werefox-blue-light ring-alice-werefox-red-dark dark:ring-alice-werefox-red hover:ring-alice-werefox-blue-dark dark:hover:ring-alice-werefox-blue hover:animate-yip transition", + summary { class: "flex flex-col space-y-2 justify-center text-center p-2", + div { class: "text-lg text-center", + span { class: "animate-jiggle sm:w-6 sm:h-6 w-4 h-6 inline-block align-top", + img { src: "{image_props.src}", alt: "{image_props.alt}" } + } + " {project_title}" + } + div { "{project_description}" } + } + {children} + } + } +} diff --git a/src/lib.rs b/src/lib.rs index f2ec4e7..ded105e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,12 +1,9 @@ -//! # Rust Info Site Frontend +//! # Rust Info Site //! //! Rendering functions for the site using [Dioxus](https://dioxuslabs.com/). #![allow(non_snake_case)] -// use utils::{prop_structs::PoemDatabase, user_prefs::UserPrefs}; -// use manganis; - pub mod components; pub mod utils; @@ -20,26 +17,17 @@ pub mod info_app { // import the prelude to get access to the `rsx!` macro and the `Scope` and `Element` types pub use dioxus::prelude::*; - // use crate::components::void_buttons::*; - // use crate::components::void_content::*; - // use crate::components::void_footer::*; use crate::components::basic_page::*; use crate::components::home_page::*; use crate::components::identity_button::*; use crate::components::introduction_card::*; use crate::components::page_button::*; use crate::components::project_card::*; - use crate::components::title_card::*; use crate::components::werefox_card::*; + use crate::components::project_foldable::*; use crate::utils::prop_structs::*; - // use crate::components::void_poem::*; - // use crate::components::void_title::*; - // use crate::utils::helpers; - // use crate::utils::user_prefs::*; - // use crate::{components::basic_page::*, utils::prop_structs::PageProps}; use dioxus_router::prelude::*; - // use dioxus_use_storage::use_local_storage; #[derive(Routable, PartialEq, Clone)] pub enum Route { @@ -61,14 +49,6 @@ pub mod info_app { rsx! { Router:: {} } } - // #[cfg(target_family = "wasm")] - // fn PageNotFound() -> Element { - // rsx! { - // p { "That page doesn't exist, sorry!" } - - // } - // } - #[component] /// Renders the app and returns the rendered Element. pub fn Home() -> Element { @@ -200,6 +180,15 @@ pub mod info_app { alt: "Pen emoji".to_string(), }, ), + ( + "Valentine's Day Letter", + "Here's a little treat I put together for Valentine's Day one year, and I decided to just keep it up all year. Feel free to take a look if you ever need a little pick-me-up.", + "https://letter.werefox.cafe", + ImageProps { + src: "/emoji/red_heart.svg".to_string(), + alt: "Red heart emoji".to_string(), + }, + ), ( "Programming", "Much like this site, sometimes I program things! Gotta at least try and put that Comp. Sci. degree to good use!", @@ -209,13 +198,116 @@ pub mod info_app { alt: "Laptop emoji".to_string(), }, ), + ]; + let service_list = [ ( - "Valentine's Day Letter", - "Here's a little treat I put together for Valentine's Day one year, and I decided to just keep it up all year. Feel free to take a look if you ever need a little pick-me-up.", - "https://letter.werefox.cafe", + "Nextcloud", + None, + "https://cloud.werefox.cafe", ImageProps { - src: "/emoji/red_heart.svg".to_string(), - alt: "Red heart emoji".to_string(), + src: "/emoji/twitch-logo.png".to_string(), + alt: "Twitch logo".to_string(), + }, + ), + ( + "Gitea", + None, + "https://gitea.werefox.cafe", + ImageProps { + src: "/emoji/twitch-logo.png".to_string(), + alt: "Twitch logo".to_string(), + }, + ), + ( + "GoToSocial", + None, + "https://gts.werefox.cafe", + ImageProps { + src: "/emoji/twitch-logo.png".to_string(), + alt: "Twitch logo".to_string(), + }, + ), + ( + "Headscale", + Some("(A self-hosted tailscale server)".to_string()), + "https://headscale.net/", + ImageProps { + src: "/emoji/twitch-logo.png".to_string(), + alt: "Twitch logo".to_string(), + }, + ), + ( + "Jellyfin", + None, + "https://watch.werefox.cafe", + ImageProps { + src: "/emoji/twitch-logo.png".to_string(), + alt: "Twitch logo".to_string(), + }, + ), + ( + "Matrix", + None, + "https://matrix.werefox.cafe", + ImageProps { + src: "/emoji/twitch-logo.png".to_string(), + alt: "Twitch logo".to_string(), + }, + ), + ( + "Navidrome", + None, + "https://music.werefox.cafe", + ImageProps { + src: "/emoji/twitch-logo.png".to_string(), + alt: "Twitch logo".to_string(), + }, + ), + ( + "Tunic Transition Tracker", + Some("(A tracker I made for a game I love)".to_string()), + "https://tunic.werefox.cafe", + ImageProps { + src: "/emoji/twitch-logo.png".to_string(), + alt: "Twitch logo".to_string(), + }, + ), + ]; + let personal_list = [ + ( + "Cockpit", + None, + "https://cockpit-project.org/", + ImageProps { + src: "/emoji/twitch-logo.png".to_string(), + alt: "Twitch logo".to_string(), + }, + ), + ( + "Dockge", + None, + "https://dockge.kuma.pet/", + ImageProps { + src: "/emoji/twitch-logo.png".to_string(), + alt: "Twitch logo".to_string(), + }, + ), + ( + "Home Assistant", + None, + "https://www.home-assistant.io/", + ImageProps { + src: "/emoji/twitch-logo.png".to_string(), + alt: "Twitch logo".to_string(), + }, + ), + ( + "Pi-Hole", + None, + "https://pi-hole.net/", + ImageProps { + src: "/emoji/twitch-logo.png".to_string(), + alt: "Twitch logo".to_string(), }, ), ]; @@ -226,6 +318,26 @@ pub mod info_app { for project in project_list { ProjectCard { project_title: project.0, project_description: project.1, project_link: project.2, image_props: project.3 } } + ProjectFoldable { project_title: "Services", + project_description: "Click here for a list of the services I host.", + image_props: ImageProps { + src: "/emoji/crt_blue_screen.svg".to_string(), + alt: "A CRT blue screen emoji.".to_string(), + } + for service in service_list { + ProjectCard { project_title: service.0, project_description: service.1, project_link: service.2, image_props: service.3 } + } + } + ProjectFoldable { project_title: "Personal Use", + project_description: "Click here for a list of services I have set up for just me.", + image_props: ImageProps { + src: "/emoji/crt_blue_screen.svg".to_string(), + alt: "A CRT blue screen emoji.".to_string(), + } + for personal in personal_list { + ProjectCard { project_title: personal.0, project_description: personal.1, project_link: personal.2, image_props: personal.3 } + } + } } } } @@ -251,106 +363,4 @@ pub mod info_app { pub fn Support() -> Element { rsx! { div {} } } - - // Renders the app and returns the rendered Element. - // pub fn PoemListPage(props: VoidProps) -> Element { - // #[cfg(target_family = "wasm")] - // let poem_database = POEM_DATABASE - // .get() - // .expect("Poem database is not initialized") - // .clone(); - // #[cfg(any(target_family = "unix", target_family = "windows"))] - // let poem_database = props.poem_database.clone(); - // let user_prefs = props.user_prefs.clone(); - // let (user_theme, user_font) = user_prefs.get_pref_classes(ThemedComponent::Page); - // rsx! { - // div { class: "{user_theme} {user_font}", - // PageBase { - // Title { title: "A Letter to the Void".to_string(), is_html: false, user_prefs: user_prefs.clone() } - // BackToHomePage { theme: user_prefs.clone().get_theme(), font: user_prefs.clone().get_font() } - // PoemList { poem_database: poem_database.clone(), user_prefs: user_prefs.clone() } - // BackToHomePage { theme: user_prefs.clone().get_theme(), font: user_prefs.clone().get_font() } - // Footer { theme: user_prefs.clone().get_theme(), font: user_prefs.get_font() } - // } - // } - // } - // } - - // pub fn PoemPage(props: VoidProps) -> Element { - // #[cfg(target_family = "wasm")] - // let poem_database = POEM_DATABASE - // .get() - // .expect("Poem database is not initialized") - // .clone(); - // #[cfg(any(target_family = "unix", target_family = "windows"))] - // let poem_database = props.poem_database.clone(); - // let user_prefs = props.user_prefs.clone(); - // let (user_theme, user_font) = user_prefs.get_pref_classes(ThemedComponent::Page); - // // #[cfg(any(target_family = "unix", target_family = "windows"))] - // let slug = props - // .slug - // .as_ref() - // .expect("A slug was given in the props.") - // .clone(); - // // #[cfg(target_family = "wasm")] - // // let slug = String::from( - // // use_route() - // // .segment("slug") - // // .expect("No slug specified."), - // // ); - // rsx! { - // div { class: "{user_theme} {user_font}", - // PageBase { - // BackToHomePage { theme: user_prefs.clone().get_theme(), font: user_prefs.clone().get_font() } - // GetPoem { slug: slug.clone(), poem_database: poem_database.clone(), user_prefs: user_prefs.clone() } - // ButtonGroup { - // NavigationButton { title: "Oldest".to_string(), slug: poem_database.get_oldest_entry(slug.clone()), user_prefs: user_prefs.clone() } - // NavigationButton { title: "Previous".to_string(), slug: poem_database.get_previous_entry(slug.clone()), user_prefs: user_prefs.clone() } - // NavigationButton { title: "Random".to_string(), slug: poem_database.get_random_entry(), user_prefs: user_prefs.clone() } - // NavigationButton { title: "Next".to_string(), slug: poem_database.get_next_entry(slug.clone()), user_prefs: user_prefs.clone() } - // NavigationButton { title: "Latest".to_string(), slug: poem_database.get_latest_entry(slug.clone()), user_prefs: user_prefs.clone() } - // } - // BackToHomePage { theme: user_prefs.clone().get_theme(), font: user_prefs.clone().get_font() } - // Footer { theme: user_prefs.clone().get_theme(), font: user_prefs.get_font() } - // } - // } - // } - // } - - // pub fn SettingsPage(props: UserPrefs) -> Element { - // let user_prefs = props.clone(); - // let (user_theme, user_font) = user_prefs.get_pref_classes(ThemedComponent::Page); - // // Get rid of this and create a general card component with children. - // let (user_theme_card, user_font_card) = user_prefs.get_pref_classes(ThemedComponent::Card); - // rsx! { - // div { class: "{user_theme} {user_font}", - // PageBase { - // Title { title: "Settings".to_string(), is_html: false, user_prefs: user_prefs.clone() } - // BackToHomePage { theme: user_prefs.clone().get_theme(), font: user_prefs.clone().get_font() } - // div { class: "p-4 flex flex-col space-y-4 mx-auto max-w-full justify-center", - // div { class: "p-4 flex flex-col space-y-4 text-xl text-center ring-4 {user_theme_card} {user_font_card}", - // p { - // "Theme" - // } - // ButtonGroup { - // NavigationButton { title: "Light".to_string(), slug: "/settings/?theme=light".to_string(), user_prefs: user_prefs.clone() } - // NavigationButton { title: "Dark".to_string(), slug: "/settings/?theme=dark".to_string(), user_prefs: user_prefs.clone() } - // NavigationButton { title: "Auto".to_string(), slug: "/settings/?theme=auto".to_string(), user_prefs: user_prefs.clone() } - // } - // } - // div { class: "p-4 flex flex-col space-y-4 text-xl text-center ring-4 {user_theme_card} {user_font_card}", - // p { - // "Font" - // } - // ButtonGroup { - // NavigationButton { title: "Nerd Font".to_string(), slug: "/settings/?font=nerd".to_string(), user_prefs: user_prefs.clone(), override_font: "font-nerd".to_string() } - // NavigationButton { title: "Open Dyslexic".to_string(), slug: "/settings/?font=open".to_string(), user_prefs: user_prefs.clone(), override_font: "font-open".to_string() } - // } - // } - // } - // BackToHomePage { theme: user_prefs.clone().get_theme(), font: user_prefs.get_font() } - // } - // } - // } - // } }