use crate::{set_meta_tags, Route}; use dioxus::{logger::tracing, prelude::*}; use reqwest; use serde::{Deserialize, Serialize}; // const BLOG_CSS: Asset = asset!("/assets/styling/blog.css"); /// The Blog page component that will be rendered when the current route is `[Route::Blog]` /// /// The component takes a `id` prop of type `i32` from the route enum. Whenever the id changes, the component function will be /// re-run and the rendered HTML will be updated. #[component] pub fn Blog(blog_title: String) -> Element { let blog_content = use_signal(move || { "

This is a blog #blog_title

In blog #{blog_title}, we show how the Dioxus router works and how URL parameters can be passed as props to our route components.

" .to_string() }); // let blog_content = use_signal(move || BlogContent { // blog_file_name: "blog_title".to_string(), // blog_title: "This is a blog #blog_title".to_string(), // date_last_edit: "2025-5-20".to_string(), // tags: "#test".to_string(), // html_blog_content: "

// In blog #{blog_title}, we show how the Dioxus router works and // how URL parameters can be passed as props to our route components. //

" // .to_string(), // }); let blog = blog_content(); // let last_edit = &blog.date_last_edit; // let tag = &blog.tags; rsx! { document::Stylesheet { href: asset!("/assets/styling/blog.css") } document::Title { "Brock Tomlinson - {blog_title}" } set_meta_tags { description: "{blog_title}", keywords: "blog software engineer webdev {blog_title}", } div { id: "blog", // Content // h1 { "This is blog #{blog_title}!" } // p { // "In blog #{blog_title}, we show how the Dioxus router works and how URL parameters can be passed as props to our route components." // } // // Navigation links // // The `Link` component lets us link to other routes inside our app. It takes a `to` prop of type `Route` and // // any number of child nodes. // Link { // // The `to` prop is the route that the link should navigate to. We can use the `Route` enum to link to the // // blog page with the id of -1. Since we are using an enum instead of a string, all of the routes will be checked // // at compile time to make sure they are valid. // to: Route::Blog { id: id - 1 }, // "Previous" // } // span { " <---> " } Link { to: Route::Blogs { page_num: 0 }, "Go Back" } // div { dangerous_inner_html: *&blog.html_blog_content.as_str(), // h1 { "{blog_title}" } // div { // p { "{&blog.tags}" } // p { "{&blog.date_last_edit}" } // } // } div { dangerous_inner_html: blog_content } } } } async fn get_blog(blog_name: String) { let res = reqwest::get("https://www.rust-lang.org") .await .unwrap() .text() .await .unwrap_or("".to_string()); tracing::info!("{}", res); } #[component] pub fn Blogs(page_num: u32) -> Element { let mut _num_limit: Signal = use_signal(|| 10); let blogs_resource: Resource> = use_resource(move || async move { get_blogs_preview(_num_limit(), page_num) .await .unwrap_or_else(|_| vec![]) }); rsx! { document::Stylesheet { href: asset!("/assets/styling/blog.css") } div { id: "blogs", document::Title { "Brock Tomlinson - Blogs" } set_meta_tags { description: "This is a collection of blog posts, ranging from tutorials, technologies I found interesting, and opinion pieces", keywords: "blogs blog software engineer webdev", } div { id: "blogs-title", h1 { "Blogs" } p { "This is a collection of blog posts, ranging from tutorials, technologies I found interesting, and opinion pieces" } p { "These blogs are my opinion and mine alone" } } // Link { to: Route::Home {}, // button { "Home" } // } // Link { // to: Route::Blog { // blog_title: "Test_Blog".to_string(), // }, // button { "To Test Blog" } // } div { if let Some(blogs) = &*blogs_resource.read() { if blogs.len() > 0 { for blog in blogs.iter() { Link { to: Route::Blog { blog_title: blog.blog_file_name.clone(), }, div { dangerous_inner_html: blog.html_blog_content.as_str(), h1 { "{blog.blog_title}" } div { p { "{blog.tags}" } p { "{blog.date_last_edit}" } } } } } } else { div { id: "blog-out-of", p { "No more blogs available" } Link { to: Route::Blogs { page_num: 0 }, button { "Go Back" } } } } } else { div { id: "blog-loading", p { "Loading blogs..." } } } } div { id: "blog-nav", if page_num > 0 { Link { to: Route::Blogs { page_num: page_num - 1, }, button { "<-- Go Back" } } } div { label { "display: " } select { onchange: move |event| { tracing::info!("Change happened {:?}", event.value()); _num_limit.set(event.value().parse::().unwrap_or(10)); }, option { "10" } option { "25" } option { "50" } option { "100" } } } Link { to: Route::Blogs { page_num: page_num + 1, }, button { "Next -->" } } } } } } #[derive(Deserialize, Serialize, Debug)] struct BlogContent { pub blog_file_name: String, pub date_last_edit: String, pub blog_title: String, pub tags: String, pub html_blog_content: String, } async fn get_blogs_preview( _num_limit: u8, page_num: u32, ) -> Result, reqwest::Error> { let client = reqwest::Client::new(); let res: String = client .get(format!( "http://localhost:8000/blogs/{}/{}", _num_limit, page_num )) .timeout(std::time::Duration::from_secs(10)) .send() .await? .text() .await?; let json: serde_json::Value = serde_json::from_str(&res).unwrap(); // Extract the "Blogs" array and deserialize it into Vec let blogs: Vec = serde_json::from_value( json.get("Blogs") .cloned() .unwrap_or(serde_json::Value::Null), ) .unwrap_or_default(); // tracing::info!("{:?}", blogs); Ok(blogs) }