diff --git a/Cargo.lock b/Cargo.lock index 7cf5389..1109031 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -114,6 +114,7 @@ dependencies = [ "lazy_static", "manganis", "serde", + "tokio", "tracing", ] diff --git a/Cargo.toml b/Cargo.toml index 04df9ed..2d0cdae 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,7 @@ edition = "2021" [dependencies] serde = { version = "1.0.197", features = ["derive"] } dioxus = { version = "0.5", features = ["fullstack", "router"] } +tokio = { version = "1", features = ["full"], optional = true } # Debug tracing = "0.1.40" diff --git a/Dockerfile b/Dockerfile index 1722d77..76d08b4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM rust:1.79.0 AS dioxus +FROM rust:1.80.1 AS dioxus RUN cargo install dioxus-cli@^0.5 FROM dioxus AS builder diff --git a/assets/tailwind.css b/assets/tailwind.css index e1f32ae..52ddfd2 100644 --- a/assets/tailwind.css +++ b/assets/tailwind.css @@ -608,15 +608,6 @@ video { inset-inline-start: -0.375rem; } -.m-16 { - margin: 4rem; -} - -.mx-16 { - margin-left: 4rem; - margin-right: 4rem; -} - .mx-auto { margin-left: auto; margin-right: auto; @@ -632,16 +623,6 @@ video { margin-bottom: 2rem; } -.my-16 { - margin-top: 4rem; - margin-bottom: 4rem; -} - -.mx-8 { - margin-left: 2rem; - margin-right: 2rem; -} - .mb-1 { margin-bottom: 0.25rem; } @@ -690,10 +671,6 @@ video { display: flex; } -.h-16 { - height: 4rem; -} - .h-24 { height: 6rem; } @@ -718,10 +695,6 @@ video { min-height: 100vh; } -.w-16 { - width: 4rem; -} - .w-24 { width: 6rem; } @@ -791,14 +764,14 @@ video { justify-content: space-between; } -.gap-4 { - gap: 1rem; -} - .gap-2 { gap: 0.5rem; } +.gap-4 { + gap: 1rem; +} + .space-x-4 > :not([hidden]) ~ :not([hidden]) { --tw-space-x-reverse: 0; margin-right: calc(1rem * var(--tw-space-x-reverse)); @@ -817,12 +790,6 @@ video { margin-bottom: calc(0.5rem * var(--tw-space-y-reverse)); } -.space-y-4 > :not([hidden]) ~ :not([hidden]) { - --tw-space-y-reverse: 0; - margin-top: calc(1rem * calc(1 - var(--tw-space-y-reverse))); - margin-bottom: calc(1rem * var(--tw-space-y-reverse)); -} - .space-y-8 > :not([hidden]) ~ :not([hidden]) { --tw-space-y-reverse: 0; margin-top: calc(2rem * calc(1 - var(--tw-space-y-reverse))); @@ -1305,11 +1272,6 @@ video { } @media (min-width: 640px) { - .sm\:my-16 { - margin-top: 4rem; - margin-bottom: 4rem; - } - .sm\:mt-0 { margin-top: 0px; } @@ -1318,8 +1280,10 @@ video { flex-direction: row; } - .sm\:items-center { - align-items: center; + .sm\:space-x-8 > :not([hidden]) ~ :not([hidden]) { + --tw-space-x-reverse: 0; + margin-right: calc(2rem * var(--tw-space-x-reverse)); + margin-left: calc(2rem * calc(1 - var(--tw-space-x-reverse))); } .sm\:space-y-0 > :not([hidden]) ~ :not([hidden]) { @@ -1328,12 +1292,6 @@ video { margin-bottom: calc(0px * var(--tw-space-y-reverse)); } - .sm\:space-x-8 > :not([hidden]) ~ :not([hidden]) { - --tw-space-x-reverse: 0; - margin-right: calc(2rem * var(--tw-space-x-reverse)); - margin-left: calc(2rem * calc(1 - var(--tw-space-x-reverse))); - } - .sm\:text-center { text-align: center; } diff --git a/src/components/mod.rs b/src/components/mod.rs index 0e70e4b..8669da8 100644 --- a/src/components/mod.rs +++ b/src/components/mod.rs @@ -142,3 +142,105 @@ pub fn HR() -> Element { hr { class:"h-px my-8 bg-gray-200 border-0 dark:bg-gray-700"} } } + +#[derive(Clone, PartialEq, Props)] +pub struct BoldingProp { + authors: String, + patterns: Vec, +} + +pub fn Bolding(prop: BoldingProp) -> Element { + let mut elements = vec![]; + let mut last_index = 0; + let authors_text = &prop.authors; + + let mut matches: Vec<(usize, usize, &str)> = vec![]; + + for pattern in &prop.patterns { + for (start, _) in authors_text.match_indices(pattern) { + matches.push((start, start + pattern.len(), pattern)); + } + } + + matches.sort_by_key(|(start, _, _)| *start); + + for (start, end, matched_pattern) in matches { + if last_index < start { + elements.push(rsx! { "{&authors_text[last_index..start]}" }); + } + + elements.push(rsx! { b { "{matched_pattern}" } }); + last_index = end; + } + + if last_index < authors_text.len() { + elements.push(rsx! { "{&authors_text[last_index..]}" }); + } + + rsx! { + div { + P { + for i in elements { + {i} + } + } + } + } +} + +#[derive(Clone, PartialEq, Props)] +pub struct UrlingProp { + #[props(default = "".to_string())] + class: String, + text: String, + patterns: Vec, + url: String, + #[props(default = true)] + new_tab: bool, +} + +pub fn Urling(prop: UrlingProp) -> Element { + let mut elements = vec![]; + let mut last_index = 0; + let text = &prop.text; + + let mut matches: Vec<(usize, usize, &str)> = vec![]; + + for pattern in &prop.patterns { + for (start, _) in text.match_indices(pattern) { + matches.push((start, start + pattern.len(), pattern)); + } + } + + matches.sort_by_key(|(start, _, _)| *start); + + for (start, end, matched_pattern) in matches { + if last_index < start { + elements.push(rsx! { "{&text[last_index..start]}" }); + } + + elements.push(rsx! { + Link { + class: "{prop.class}", + to: "{prop.url}", + new_tab: prop.new_tab, + "{matched_pattern}" + } + }); + last_index = end; + } + + if last_index < text.len() { + elements.push(rsx! { "{&text[last_index..]}" }); + } + + rsx! { + div { + P { + for i in elements { + {i} + } + } + } + } +} diff --git a/src/consulting.rs b/src/consulting.rs deleted file mode 100644 index 920ed43..0000000 --- a/src/consulting.rs +++ /dev/null @@ -1,10 +0,0 @@ -use dioxus::prelude::*; - -use crate::components::UnderConstruction; - -#[component] -pub fn Consulting() -> Element { - rsx! { - UnderConstruction { }, - } -} diff --git a/src/home.rs b/src/home.rs index 2cf72cb..010316b 100644 --- a/src/home.rs +++ b/src/home.rs @@ -1,10 +1,11 @@ -use crate::components::{Card, P}; +use crate::components::{Card, Urling, P}; use dioxus::prelude::*; use dioxus_sdk::{i18n::use_i18, translate}; #[component] pub fn Home() -> Element { let i18 = use_i18(); + rsx! { div { class: "container mx-auto p-4 flex items-center justify-center max-w-md w-full", @@ -16,17 +17,16 @@ pub fn Home() -> Element { class: "py-4", div { class: "mb-2", - P { { translate!(i18, "home.card.l1") } }, - P { { translate!(i18, "home.card.l2") } }, - P { { translate!(i18, "home.card.l3") } }, - P { { translate!(i18, "home.card.l4") } - Link { - to: "https://git.tudattr.dev/explore/repos", - new_tab: true, - class: "items-center font-medium hover:underline", - "gitea"}, - { translate!(i18, "home.card.l4_1") } - }, + for line in translate!(i18, "home.card.text").split("\n").into_iter() { + P { + Urling { + class: "items-center font-medium hover:underline", + text: line, + patterns: vec!["Gitea".to_string()], + url: "https://git.tudattr.dev/explore/repos" + } + } + } }, }, Link { diff --git a/src/impressum.rs b/src/impressum.rs index a4f7127..3134acc 100644 --- a/src/impressum.rs +++ b/src/impressum.rs @@ -1,48 +1,60 @@ use dioxus::prelude::*; use dioxus_sdk::{i18n::use_i18, translate}; -use tracing::info; use crate::components::{H1, HR, P}; #[component] pub fn Impressum() -> Element { - let mut show_impressum = use_signal(|| false); let i18 = use_i18(); + let mut impressum = use_signal(Vec::::new); + let mut contact = use_signal(Vec::::new); rsx! { - if show_impressum() { + div { div { - div { - class: "flex flex-col items-center", - button { - onclick: move |_| { - info!("Hide impressum."); - }, - H1 { { translate!(i18, "impressum.on") } }, - }, - P { "Tuan-Dat Tran" }, - P { "c/o AutorenServices.de" }, - P { "Birkenallee 24" }, - P { "36037 Fulda" }, - }, - HR {} - div { - class: "flex flex-col items-center", - P { "tuan-dat.tran@tudattr.dev" }, - P { "+49 176 83468388" }, - } - } - } else { - div { - class: "flex flex-col items-center p-3", + class: "flex flex-col items-center", button { onclick: move |_| async move { - show_impressum.set(true); + if let Ok(data) = get_impressum().await { + impressum.set(data.clone()); + } + if let Ok(data) = get_contact().await { + contact.set(data.clone()); + } }, - - H1 { { translate!(i18, "impressum.off") } }, + H1 { { translate!(i18, "impressum.on") } }, }, + }, + div { + class: "flex flex-col items-center", + for line in impressum() { + P { {line} } + } + } + if !impressum.read().is_empty() { HR{} }, + div { + class: "flex flex-col items-center", + for line in contact() { + P { {line} } + } } } } } + +#[server(GetServerData)] +async fn get_impressum() -> Result, ServerFnError> { + Ok(vec![ + "Tuan-Dat Tran".to_string(), + "c/o AutorenServices.de".to_string(), + "Birkenallee 24".to_string(), + "36037 Fulda".to_string(), + ]) +} + +async fn get_contact() -> Result, ServerFnError> { + Ok(vec![ + "tuan-dat.tran@tudattr.dev".to_string(), + "+49 176 83468388".to_string(), + ]) +} diff --git a/src/languages/de-DE.json b/src/languages/de-DE.json index 5bf1b23..a6fa17d 100644 --- a/src/languages/de-DE.json +++ b/src/languages/de-DE.json @@ -16,11 +16,7 @@ "card": { "name": "Tuan-Dat Tran", "gender": "", - "l1": "Hallihallo! πŸ‘‹πŸ»πŸ‘‹πŸΌπŸ‘‹πŸ½πŸ‘‹πŸΎπŸ‘‹πŸΏ", - "l2": "Willkommen auf meiner kleinen Webseite im World Wide Web.", - "l3": "Mein Name ist Tuan und ich bin Linux-Bastler, IT-Security Nerd und Automatisierer aus Leidenschaft.", - "l4": "WΓ€hrend du hier bist, schau dir doch meine Projekte auf ", - "l4_1": " an.", + "text": "Hallihallo! πŸ‘‹πŸ»πŸ‘‹πŸΌπŸ‘‹πŸ½πŸ‘‹πŸΎπŸ‘‹πŸΏ\nWillkommen auf meiner kleinen Webseite im World Wide Web.\nMein Name ist Tuan und ich bin Linux-Bastler, IT-Security Nerd und Automatisierer aus Leidenschaft.\nWΓ€hrend du hier bist, schau dir doch meine Projekte auf Gitea an.\n", "contact_button": "Get in touch." } }, diff --git a/src/languages/en-GB.json b/src/languages/en-GB.json index 888fcc9..957fbaf 100644 --- a/src/languages/en-GB.json +++ b/src/languages/en-GB.json @@ -3,7 +3,7 @@ "texts": { "headers": { "home": "Home", - "cv": "CV", + "cv": "RΓ©sumΓ©", "publications_projects": "Publications/Projects", "consulting": "Consulting", "about": "About", @@ -16,11 +16,7 @@ "card": { "name": "Tuan-Dat Tran", "gender": "(He/Him)", - "l1": "Hey there! πŸ‘‹πŸ»πŸ‘‹πŸΌπŸ‘‹πŸ½πŸ‘‹πŸΎπŸ‘‹πŸΏ", - "l2": "Welcome to my little place on the internet.", - "l3": "My name is Tuan and I'm passionate about Linux, system security, automation, network performance tweaking and all things tech.", - "l4": "While you're here, why don't you check out my projects over on ", - "l4_1": "?", + "text": "Hey there! πŸ‘‹πŸ»πŸ‘‹πŸΌπŸ‘‹πŸ½πŸ‘‹πŸΎπŸ‘‹πŸΏ\nWelcome to my little place on the internet\nMy name is Tuan and I'm passionate about Linux, system security, automation, network performance tweaking and all things tech.\nWhile you're here, why don't you check out my projects over on Gitea?", "contact_button": "Get in touch." } }, diff --git a/src/layout/footer.rs b/src/layout/footer.rs index 3dab258..628f162 100644 --- a/src/layout/footer.rs +++ b/src/layout/footer.rs @@ -1,15 +1,12 @@ use dioxus::prelude::*; use dioxus_sdk::{i18n::use_i18, translate}; -use crate::components::H1; - pub fn Footer() -> Element { let i18 = use_i18(); rsx! { div { - class: "container mx-auto", - // ToolsUsed {}, + class: "container mx-auto pb-4", footer { class:"bg-white rounded-lg shadow dark:bg-gray-800", div { @@ -32,42 +29,6 @@ pub fn Footer() -> Element { } } -#[allow(dead_code)] -fn ToolsUsed() -> Element { - rsx! { - div { - class:"items-center bg-white rounded-lg shadow dark:bg-gray-800 p-4 my-4", - div { - H1 {class: "justify-center", "Tools used" }, - } - div { - class: "flex h-fill overflow-x-auto", - Logo { - src: "https://raw.githubusercontent.com/SAWARATSUKI/Logogs/main/Rust/Rust.png", - alt: "Rust" - }, - Logo { - src: "https://raw.githubusercontent.com/SAWARATSUKI/Logogs/main/Tailwindcss/Tailwindcss6.png", - alt: "Tailwindcss" - }, - Logo { - src: "https://raw.githubusercontent.com/SAWARATSUKI/Logogs/main/Html/HTML.png", - alt: "HTML" - }, - Logo { - src: "https://raw.githubusercontent.com/Aikoyori/ProgrammingVTuberLogos/main/Docker/DockerLogo.png", - alt: "Docker" - }, - Logo { - src: "https://raw.githubusercontent.com/Aikoyori/ProgrammingVTuberLogos/main/Neovim/NeovimLogo.png", - alt: "NeoVim" - }, - - } - } - } -} - #[component] fn Logo(src: String, alt: String) -> Element { rsx! { diff --git a/src/layout/header.rs b/src/layout/header.rs index cb318f8..dfe3feb 100644 --- a/src/layout/header.rs +++ b/src/layout/header.rs @@ -9,13 +9,10 @@ pub fn Header() -> Element { rsx! { nav { div { - // class: "justify-between p-4 space-x-8", - class: "container mx-auto p-4", + class: "container mx-auto py-4", ul { - class:"flex flex-col justify-between items-center sm:flex-row justify-center space-y-2 sm:space-y-0 sm:space-s-4", - - // class:"flex flex-col sm:flex-row justify-center items-center sm:space-x-8 space-y-8 sm:space-y-0", + class:"flex flex-col justify-between items-center justify-center space-y-2 sm:flex-row sm:space-y-0 sm:space-s-4", li { Link { to: Route::Home {}, @@ -26,7 +23,6 @@ pub fn Header() -> Element { li { HeaderLink { url: Route::Home {}, text: translate!(i18, "headers.home")} }, li { HeaderLink { url: Route::CV {}, text: translate!(i18, "headers.cv") } }, li { HeaderLink { url: Route::PublicationsProjects {}, text: translate!(i18, "headers.publications_projects") } }, - li { HeaderLink { url: Route::Consulting {}, text: translate!(i18, "headers.consulting") } }, li { HeaderLink { url: Route::Impressum {}, text: translate!(i18, "headers.about") } }, li { LanguageButtonGroup {} }, }, @@ -44,9 +40,15 @@ fn LanguageButtonGroup() -> Element { rsx! { div { - class: "rounded-md shadow-sm justify-end", - button { class: "px-4 py-2 text-sm font-medium text-gray-900 bg-white border border-gray-200 rounded-s-lg hover:bg-gray-100 hover:text-blue-700 focus:z-10 focus:ring-2 focus:ring-blue-700 focus:text-blue-700 dark:bg-gray-800 dark:border-gray-700 dark:text-white dark:hover:text-white dark:hover:bg-gray-700 dark:focus:ring-blue-500 dark:focus:text-white", onclick: change_to_english, label { { translate!(i18, "headers.language_buttons.english") } } }, - button { class: "px-4 py-2 text-sm font-medium text-gray-900 bg-white border border-gray-200 rounded-e-lg hover:bg-gray-100 hover:text-blue-700 focus:z-10 focus:ring-2 focus:ring-blue-700 focus:text-blue-700 dark:bg-gray-800 dark:border-gray-700 dark:text-white dark:hover:text-white dark:hover:bg-gray-700 dark:focus:ring-blue-500 dark:focus:text-white", onclick: change_to_german, label { { translate!(i18, "headers.language_buttons.german") } } } + class: "rounded-md shadow-sm justify-end", + button { + class: "px-4 py-2 text-sm font-medium text-gray-900 bg-white border border-gray-200 rounded-s-lg hover:bg-gray-100 hover:text-blue-700 focus:z-10 focus:ring-2 focus:ring-blue-700 focus:text-blue-700 dark:bg-gray-800 dark:border-gray-700 dark:text-white dark:hover:text-white dark:hover:bg-gray-700 dark:focus:ring-blue-500 dark:focus:text-white", + onclick: change_to_english, + label { { translate!(i18, "headers.language_buttons.english") } } }, + button { + class: "px-4 py-2 text-sm font-medium text-gray-900 bg-white border border-gray-200 rounded-e-lg hover:bg-gray-100 hover:text-blue-700 focus:z-10 focus:ring-2 focus:ring-blue-700 focus:text-blue-700 dark:bg-gray-800 dark:border-gray-700 dark:text-white dark:hover:text-white dark:hover:bg-gray-700 dark:focus:ring-blue-500 dark:focus:text-white", + onclick: change_to_german, + label { { translate!(i18, "headers.language_buttons.german") } } } } } } diff --git a/src/layout/mod.rs b/src/layout/mod.rs index fea89e3..66daaec 100644 --- a/src/layout/mod.rs +++ b/src/layout/mod.rs @@ -14,8 +14,8 @@ pub fn Layout() -> Element { Header {}, Body { Outlet:: {}, - Footer {}, - } + }, + Footer {} } } } diff --git a/src/main.rs b/src/main.rs index 7bd5a6e..c9d69ae 100644 --- a/src/main.rs +++ b/src/main.rs @@ -11,7 +11,6 @@ use layout::header::Header; use tracing::Level; pub mod components; -mod consulting; mod cv; mod home; mod impressum; @@ -19,7 +18,6 @@ mod languages; mod layout; mod publications; -use crate::consulting::Consulting; use crate::cv::CV; use crate::home::Home; use crate::impressum::Impressum; @@ -39,8 +37,6 @@ pub enum Route { PublicationsProjects {}, #[route("/resume")] CV {}, - #[route("/consulting")] - Consulting {}, #[end_layout] #[route("/:..route")] PageNotFound { route: Vec }, diff --git a/src/publications.rs b/src/publications.rs index 6cdb80c..815c733 100644 --- a/src/publications.rs +++ b/src/publications.rs @@ -1,7 +1,7 @@ use dioxus::prelude::*; use dioxus_sdk::{i18n::use_i18, translate}; -use crate::components::{UnderConstruction, H1, HR}; +use crate::components::{Bolding, UnderConstruction, H1, HR}; #[component] pub fn PublicationsProjects() -> Element { @@ -60,7 +60,7 @@ fn Publications() -> Element { } } fn Publication(prop: PublicationProp) -> Element { - let pattern = "T.-D. Tran"; + let pattern = vec!["T.-D. Tran".to_string(), "Tuan-Dat Tran".to_string()]; rsx! { Link { class:"block max-w-sm p-6 bg-white border border-gray-200 rounded-lg shadow hover:bg-gray-100 dark:bg-gray-800 dark:border-gray-700 dark:hover:bg-gray-700", @@ -73,9 +73,9 @@ fn Publication(prop: PublicationProp) -> Element { span { class: "text-lg text-gray-900 dark:text-white", "{prop.conference}" }, p { class:"font-normal text-gray-700 dark:text-gray-400 italic", - Authors { + Bolding { authors: "{prop.authors}", - pattern: "{pattern}", + patterns: pattern, }, } p { @@ -129,7 +129,7 @@ struct ProjectProp { } fn Project(prop: ProjectProp) -> Element { - let pattern = "T.-D. Tran"; + let pattern = vec!["T.-D. Tran".to_string(), "Tuan-Dat Tran".to_string()]; rsx! { Link { @@ -143,9 +143,9 @@ fn Project(prop: ProjectProp) -> Element { p { class: "text-lg text-gray-900 dark:text-white", "{prop.kind}" }, p { class:"font-normal text-gray-700 dark:text-gray-400", - Authors { + Bolding { authors: "{prop.authors}", - pattern: "{pattern}", + patterns: pattern, }, } p { @@ -155,23 +155,3 @@ fn Project(prop: ProjectProp) -> Element { } } } - -#[derive(Clone, PartialEq, Props)] -struct AuthorProp { - authors: String, - pattern: String, -} - -fn Authors(prop: AuthorProp) -> Element { - if let Some(start) = prop.authors.find(&prop.pattern) { - let end = start + prop.pattern.len(); - let left = &prop.authors[..start]; - let middle = &prop.authors[start..end]; - let right = &prop.authors[end..]; - rsx! { - "{left}" , b { "{middle}" }, "{right}", - } - } else { - rsx! { "{prop.authors}" } - } -}