Compare commits
8 Commits
bff6a997be
...
master
Author | SHA1 | Date | |
---|---|---|---|
c14501061f | |||
5e52c6dea1 | |||
2806d654a7 | |||
9bc7175e5b | |||
ff6f6c8424 | |||
e249c1acc2 | |||
f22456a72f | |||
1bd70c4eb2 |
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -3179,7 +3179,7 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
|
||||
|
||||
[[package]]
|
||||
name = "personal_site"
|
||||
version = "1.6.0"
|
||||
version = "2.0.2"
|
||||
dependencies = [
|
||||
"dioxus",
|
||||
"reqwest",
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "personal_site"
|
||||
version = "1.6.0"
|
||||
version = "2.0.2"
|
||||
authors = ["darkicewolf50 <brock.tomlinson@ucalgary.ca>"]
|
||||
edition = "2021"
|
||||
|
||||
|
2
assets/robots.txt
Normal file
2
assets/robots.txt
Normal file
@ -0,0 +1,2 @@
|
||||
User-agent: *
|
||||
Disallow: /blogs/
|
@ -1,5 +1,60 @@
|
||||
#blog {
|
||||
margin-top: 50px;
|
||||
margin-top: 2svh;
|
||||
min-height: 80svh;
|
||||
}
|
||||
|
||||
#blog_info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
#blog_info ul {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
list-style-type: none;
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
#blog_info p {
|
||||
padding: 2svh 0svw;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
#blog_info h1 {
|
||||
padding: 1svh 1svw;
|
||||
margin: 2svh 0svw;
|
||||
margin-bottom: 0px;
|
||||
font-size: 2em;
|
||||
}
|
||||
|
||||
#blog_info div {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
padding: 1svh 0svw;
|
||||
border-bottom: var(--underlineTitle);
|
||||
border-radius: var(--underlineTitleBorderRadius);
|
||||
}
|
||||
|
||||
#blog_info div p {
|
||||
padding: 0px;
|
||||
margin: 0px;
|
||||
}
|
||||
#blog_info div div {
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
border: none;
|
||||
}
|
||||
#blog_info div p:last-child {
|
||||
margin-right: 10svh;
|
||||
}
|
||||
#blog_info div ul li {
|
||||
background-color: rgba(128, 0, 128, 0.2);
|
||||
border-radius: 1rem;
|
||||
padding: 0.25svh 8px;
|
||||
margin: 0svh 0.25svh;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#blog a {
|
||||
@ -11,6 +66,28 @@
|
||||
color: #91a4d2;
|
||||
}
|
||||
|
||||
#blog_content {
|
||||
padding: 2svh 0svw;
|
||||
}
|
||||
#blog_content h1,
|
||||
h2,
|
||||
h3 {
|
||||
margin: 0px;
|
||||
padding: 1svh 0svw;
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
#blog_content p {
|
||||
padding: 1svh 0svw;
|
||||
margin: 0px;
|
||||
text-wrap: wrap;
|
||||
max-width: 25rem;
|
||||
}
|
||||
|
||||
#blog #blog_content p {
|
||||
max-width: max-content;
|
||||
}
|
||||
|
||||
#blogs {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@ -50,6 +127,38 @@
|
||||
color: #91a4d2;
|
||||
}
|
||||
|
||||
.blog-preview {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-color: var(--card-background-color);
|
||||
border-radius: var(--card-border-radius);
|
||||
padding: 0svh 2svw;
|
||||
}
|
||||
|
||||
#blogs .blog-preview h1 {
|
||||
border-bottom: none;
|
||||
padding: 0svh 1svw;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
#blogs .blog-preview button {
|
||||
width: max-content;
|
||||
border: 2px solid rgba(145, 164, 210, 0.4);
|
||||
border-radius: var(--card-border-radius);
|
||||
font-size: medium;
|
||||
margin-top: auto;
|
||||
margin-bottom: 4svh;
|
||||
}
|
||||
#blogs .blog-preview #blog_info div p:last-child {
|
||||
margin-right: 0svh;
|
||||
margin-left: 2svw;
|
||||
}
|
||||
|
||||
#blogs .blog-preview #blog_info div div p:last-child {
|
||||
margin: 0svh 0.25svh;
|
||||
padding: 0.25svh 8px;
|
||||
}
|
||||
|
||||
#blogs-title {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@ -61,6 +170,15 @@
|
||||
padding: 1svh 0svw;
|
||||
}
|
||||
|
||||
#blogs-on-show {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
justify-content: center;
|
||||
padding: 2svh 0svw;
|
||||
row-gap: 2svh;
|
||||
column-gap: 2svw;
|
||||
}
|
||||
|
||||
#blog-loading {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
@ -2,6 +2,8 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-wrap: wrap;
|
||||
background-color: var(--card-background-color);
|
||||
border-radius: var(--card-border-radius);
|
||||
|
||||
/* flex-basis: 50%; */
|
||||
}
|
||||
|
@ -63,15 +63,11 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-bottom: 4svh;
|
||||
background-color: var(--card-background-color);
|
||||
border-radius: var(--card-border-radius);
|
||||
}
|
||||
|
||||
#experience div {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-evenly;
|
||||
background-color: var(--card-background-color);
|
||||
border-radius: var(--card-border-radius);
|
||||
margin: 4svh 0px;
|
||||
}
|
||||
|
@ -67,6 +67,10 @@
|
||||
filter: grayscale(100%) invert(100%) brightness(2.5);
|
||||
}
|
||||
|
||||
.project-title-info #dockerhub img {
|
||||
filter: grayscale(100%) invert(100%) brightness(2.5);
|
||||
}
|
||||
|
||||
.project-title-info img:hover {
|
||||
filter: brightness(0) saturate(100%) invert(65%) sepia(13%) saturate(733%)
|
||||
hue-rotate(187deg) brightness(95%) contrast(90%);
|
||||
@ -77,6 +81,11 @@
|
||||
hue-rotate(185deg) brightness(1.3) contrast(90%) saturate(100%);
|
||||
}
|
||||
|
||||
.project-title-info #dockerhub img:hover {
|
||||
filter: grayscale(100%) invert(165%) sepia(15%) saturate(733%)
|
||||
hue-rotate(185deg) brightness(1.3) contrast(90%) saturate(100%);
|
||||
}
|
||||
|
||||
.project-title-info div {
|
||||
display: flex;
|
||||
gap: 1svw;
|
||||
|
@ -2,8 +2,8 @@
|
||||
//! They can be used to defined common UI elements like buttons, forms, and modals. In this template, we define a Hero
|
||||
//! component to be used in our app.
|
||||
|
||||
mod hero;
|
||||
pub use hero::Hero;
|
||||
// mod hero;
|
||||
// pub use hero::Hero;
|
||||
|
||||
mod techs;
|
||||
pub use techs::TechCat;
|
||||
|
@ -9,6 +9,15 @@ pub fn get_tech_logos_from_str(used_tech: &'static str) -> Element {
|
||||
}
|
||||
}
|
||||
|
||||
#[component]
|
||||
pub fn set_meta_tags(description: &'static str, keywords: &'static str) -> Element {
|
||||
rsx! {
|
||||
document::Meta { name: "description", content: description }
|
||||
document::Meta { name: "keywords", content: keywords }
|
||||
document::Meta { name: "author", content: "Brock Tomlinson" }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Props, Clone, Copy)]
|
||||
pub struct TechDes {
|
||||
pub tech_name: &'static str,
|
||||
|
19
src/lib.rs
19
src/lib.rs
@ -1,7 +1,7 @@
|
||||
use dioxus::prelude::*;
|
||||
|
||||
// use components::Hero;
|
||||
use views::{Blog, Blogs, ContactMe, Home, Navbar, NewHome, Projects};
|
||||
use views::{Blog, Blogs, ContactMe, Home, Navbar, Projects};
|
||||
|
||||
/// Define a components module that contains all shared components for our app.
|
||||
mod components;
|
||||
@ -42,8 +42,8 @@ pub enum Route {
|
||||
#[route("/contact")]
|
||||
ContactMe {},
|
||||
|
||||
#[route("/new_home")]
|
||||
NewHome {},
|
||||
// #[route("/new_home")]
|
||||
// NewHome {},
|
||||
// PageNotFound is a catch all route that will match any route and placing the matched segments in the route field
|
||||
#[route("/:..route")]
|
||||
PageNotFound { route: Vec<String> },
|
||||
@ -53,6 +53,7 @@ pub enum Route {
|
||||
fn PageNotFound(route: Vec<String>) -> Element {
|
||||
rsx! {
|
||||
document::Stylesheet { href: asset!("/assets/styling/notFound.css") }
|
||||
document::Title { "Brock Tomlinson - Not Found" }
|
||||
div { id: "not-found",
|
||||
h1 { "Page not found" }
|
||||
p { "We are terribly sorry, but the page you requested doesn't exist." }
|
||||
@ -62,15 +63,3 @@ fn PageNotFound(route: Vec<String>) -> Element {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The server function at the endpoint "static_routes" will be called by the CLI to generate the list of static
|
||||
// routes. You must explicitly set the endpoint to `"static_routes"` in the server function attribute instead of
|
||||
// the default randomly generated endpoint.
|
||||
// #[server(endpoint = "static_routes", output = server_fn::codec::Json)]
|
||||
// async fn static_routes() -> Result<Vec<String>, ServerFnError> {
|
||||
// // The `Routable` trait has a `static_routes` method that returns all static routes in the enum
|
||||
// Ok(Route::static_routes()
|
||||
// .iter()
|
||||
// .map(ToString::to_string)
|
||||
// .collect())
|
||||
// }
|
||||
|
57
src/main.rs
57
src/main.rs
@ -13,6 +13,30 @@ fn main() {
|
||||
// The `launch` function is the main entry point for a dioxus app. It takes a component and renders it with the platform feature
|
||||
// you have enabled
|
||||
dioxus::launch(App);
|
||||
|
||||
// for compiling the app
|
||||
// dioxus::LaunchBuilder::new()
|
||||
// // Set the server config only if we are building the server target
|
||||
// .with_cfg(server_only! {
|
||||
// ServeConfig::builder()
|
||||
// // Enable incremental rendering
|
||||
// .incremental(
|
||||
// IncrementalRendererConfig::new()
|
||||
// // Store static files in the public directory where other static assets like wasm are stored
|
||||
// .static_dir(
|
||||
// std::env::current_exe()
|
||||
// .unwrap()
|
||||
// .parent()
|
||||
// .unwrap()
|
||||
// .join("public")
|
||||
// )
|
||||
// // Don't clear the public folder on every build. The public folder has other files including the wasm
|
||||
// // binary and static assets required for the app to run
|
||||
// .clear_cache(false)
|
||||
// )
|
||||
// .enable_out_of_order_streaming()
|
||||
// })
|
||||
// .launch(App);
|
||||
}
|
||||
|
||||
/// App is the main component of our app. Components are the building blocks of dioxus apps. Each component is a function
|
||||
@ -35,25 +59,14 @@ fn App() -> Element {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "ssg")]
|
||||
fn main() {
|
||||
use dioxus_fullstack::{incremental::IncrementalRendererConfig, prelude::*};
|
||||
|
||||
LaunchBuilder::new()
|
||||
.with_cfg(
|
||||
ServeConfig::builder()
|
||||
.incremental(
|
||||
IncrementalRendererConfig::new()
|
||||
.static_dir(
|
||||
std::env::current_exe()
|
||||
.unwrap()
|
||||
.parent()
|
||||
.unwrap()
|
||||
.join("public"),
|
||||
)
|
||||
.clear_cache(false),
|
||||
)
|
||||
.enable_out_of_order_streaming(),
|
||||
)
|
||||
.launch(app);
|
||||
}
|
||||
// The server function at the endpoint "static_routes" will be called by the CLI to generate the list of static
|
||||
// routes. You must explicitly set the endpoint to `"static_routes"` in the server function attribute instead of
|
||||
// the default randomly generated endpoint.
|
||||
// #[server(endpoint = "static_routes", output = server_fn::codec::Json)]
|
||||
// async fn static_routes() -> Result<Vec<String>, ServerFnError> {
|
||||
// // The `Routable` trait has a `static_routes` method that returns all static routes in the enum
|
||||
// Ok(Route::static_routes()
|
||||
// .iter()
|
||||
// .map(ToString::to_string)
|
||||
// .collect())
|
||||
// }
|
||||
|
@ -1,42 +1,37 @@
|
||||
use crate::Route;
|
||||
use dioxus::{logger::tracing, prelude::*};
|
||||
// use dioxus::logger::tracing::info;
|
||||
use dioxus::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 || {
|
||||
"<h1>This is a blog #blog_title</h1><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.
|
||||
</p>"
|
||||
.to_string()
|
||||
let blog_resource = use_resource({
|
||||
let title = blog_title.clone();
|
||||
move || {
|
||||
let value = title.clone();
|
||||
async move {
|
||||
get_blog(value).await.unwrap_or(BlogContent {
|
||||
blog_file_name: String::new(),
|
||||
date_last_edit: "9999-12-01".to_string(),
|
||||
blog_title: "Not Found".to_string(),
|
||||
tags: vec!["#error".to_string()],
|
||||
html_blog_content: "<p>Blog not found</p>".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: "<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.
|
||||
// </p>"
|
||||
// .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}" }
|
||||
document::Title { "Brock Tomlinson - {blog_title.clone()}" }
|
||||
// document::Meta { name: "author", content: "Brock Tomlinson" }
|
||||
document::Meta { name: "robots", content: "noindex, nofollow" }
|
||||
|
||||
div { id: "blog",
|
||||
|
||||
// Content
|
||||
@ -57,42 +52,88 @@ pub fn Blog(blog_title: String) -> Element {
|
||||
// }
|
||||
// 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 }
|
||||
if let Some(blog_content) = &*blog_resource.read() {
|
||||
article {
|
||||
header { id: "blog_info",
|
||||
h1 { "{blog_content.blog_title}" }
|
||||
div {
|
||||
ul {
|
||||
for tag in &blog_content.tags {
|
||||
li { "{tag}" }
|
||||
}
|
||||
}
|
||||
p { "{&blog_content.date_last_edit}" }
|
||||
}
|
||||
}
|
||||
section {
|
||||
id: "blog_content",
|
||||
dangerous_inner_html: *&blog_content.html_blog_content.as_str(),
|
||||
}
|
||||
}
|
||||
} else {
|
||||
p { "Loading..." }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
async fn get_blog(blog_name: String) -> Result<BlogContent, reqwest::Error> {
|
||||
let client = reqwest::Client::new();
|
||||
|
||||
let res = client
|
||||
.get(format!("blogs/blog/{}", blog_name))
|
||||
.timeout(std::time::Duration::from_secs(10))
|
||||
.send()
|
||||
.await?
|
||||
.json::<BlogContent>()
|
||||
// .text()
|
||||
.await?;
|
||||
|
||||
// tracing::info!("{:?}", res);
|
||||
Ok(res)
|
||||
|
||||
// to be removed
|
||||
// let blog = serde_json::from_str(&res).unwrap_or(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: "<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.
|
||||
// </p>"
|
||||
// .to_string(),
|
||||
// });
|
||||
// Ok(blog)
|
||||
// Ok(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: "<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.
|
||||
// </p>"
|
||||
// .to_string(),
|
||||
// })
|
||||
}
|
||||
|
||||
#[component]
|
||||
pub fn Blogs(page_num: u32) -> Element {
|
||||
let mut _num_limit: Signal<u8> = use_signal(|| 10);
|
||||
|
||||
let blogs_resource: Resource<Vec<BlogContent>> = use_resource(move || async move {
|
||||
get_blogs_preview(_num_limit(), page_num)
|
||||
.await
|
||||
.unwrap_or_else(|_| vec![])
|
||||
});
|
||||
let blogs_resource: Resource<Vec<BlogContent>> =
|
||||
use_resource(use_reactive!(|(_num_limit, page_num)| async move {
|
||||
get_blogs_preview(_num_limit(), page_num)
|
||||
.await
|
||||
.unwrap_or_else(|_| vec![])
|
||||
}));
|
||||
|
||||
rsx! {
|
||||
document::Stylesheet { href: asset!("/assets/styling/blog.css") }
|
||||
document::Meta { name: "robots", content: "noindex, nofollow" }
|
||||
document::Title { "Brock Tomlinson - Blogs" }
|
||||
div { id: "blogs",
|
||||
document::Title { "Brock Tomlinson - Blogs" }
|
||||
div { id: "blogs-title",
|
||||
h1 { "Blogs" }
|
||||
p {
|
||||
@ -100,31 +141,32 @@ pub fn Blogs(page_num: u32) -> Element {
|
||||
}
|
||||
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 {
|
||||
div { id: "blogs-on-show",
|
||||
if let Some(blogs) = &*blogs_resource.read() {
|
||||
if blogs.len() > 0 {
|
||||
for blog in blogs.iter() {
|
||||
|
||||
Link {
|
||||
class: "blog-preview",
|
||||
to: Route::Blog {
|
||||
blog_title: blog.blog_file_name.clone(),
|
||||
},
|
||||
div { dangerous_inner_html: blog.html_blog_content.as_str(),
|
||||
div { id: "blog_info",
|
||||
h1 { "{blog.blog_title}" }
|
||||
div {
|
||||
p { "{blog.tags}" }
|
||||
p { "{blog.date_last_edit}" }
|
||||
div {
|
||||
for tag in &blog.tags {
|
||||
p { "{tag}" }
|
||||
}
|
||||
}
|
||||
p { "{&blog.date_last_edit}" }
|
||||
}
|
||||
}
|
||||
div {
|
||||
id: "blog_content",
|
||||
dangerous_inner_html: *&blog.html_blog_content.as_str(),
|
||||
}
|
||||
button { "Read More Here" }
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -154,7 +196,6 @@ pub fn Blogs(page_num: u32) -> Element {
|
||||
label { "display: " }
|
||||
select {
|
||||
onchange: move |event| {
|
||||
tracing::info!("Change happened {:?}", event.value());
|
||||
_num_limit.set(event.value().parse::<u8>().unwrap_or(10));
|
||||
},
|
||||
option { "10" }
|
||||
@ -180,7 +221,7 @@ struct BlogContent {
|
||||
pub blog_file_name: String,
|
||||
pub date_last_edit: String,
|
||||
pub blog_title: String,
|
||||
pub tags: String,
|
||||
pub tags: Vec<String>,
|
||||
pub html_blog_content: String,
|
||||
}
|
||||
|
||||
@ -190,27 +231,23 @@ async fn get_blogs_preview(
|
||||
) -> Result<Vec<BlogContent>, reqwest::Error> {
|
||||
let client = reqwest::Client::new();
|
||||
|
||||
let res: String = client
|
||||
.get(format!(
|
||||
"http://localhost:8000/blogs/{}/{}",
|
||||
_num_limit, page_num
|
||||
))
|
||||
let res = client
|
||||
.get(format!("blogs/{}/{}", _num_limit, page_num))
|
||||
.timeout(std::time::Duration::from_secs(10))
|
||||
.send()
|
||||
.await?
|
||||
.text()
|
||||
.json::<Vec<BlogContent>>()
|
||||
// .text()
|
||||
.await?;
|
||||
Ok(res)
|
||||
// let json: serde_json::Value = serde_json::from_str(&res).unwrap();
|
||||
// let blogs: Vec<BlogContent> = serde_json::from_value(json).unwrap_or_default();
|
||||
|
||||
let json: serde_json::Value = serde_json::from_str(&res).unwrap();
|
||||
|
||||
// Extract the "Blogs" array and deserialize it into Vec<BlogContent>
|
||||
let blogs: Vec<BlogContent> = serde_json::from_value(
|
||||
json.get("Blogs")
|
||||
.cloned()
|
||||
.unwrap_or(serde_json::Value::Null),
|
||||
)
|
||||
.unwrap_or_default();
|
||||
|
||||
// tracing::info!("{:?}", blogs);
|
||||
Ok(blogs)
|
||||
// // Extract the "Blogs" array and deserialize it into Vec<BlogContent>
|
||||
// let blogs: Vec<BlogContent> = serde_json::from_value(
|
||||
// json.get("Blogs")
|
||||
// .cloned()
|
||||
// .unwrap_or(serde_json::Value::Null),
|
||||
// )
|
||||
// .unwrap_or_default();
|
||||
}
|
||||
|
@ -88,11 +88,7 @@ async fn send_message(name: String, email: String, message: String, mut recived:
|
||||
});
|
||||
|
||||
let client = Client::new();
|
||||
let res = client
|
||||
.post("https://discord.com/api/webhooks/1371617469281861772/uARm18pvzzs4DVNLSYNYCyl7CQk_7eglqGmBabQASow2L7NHgGRHzQhkSAKaOIZmLnn1")
|
||||
.json(&json_to_send)
|
||||
.send()
|
||||
.await;
|
||||
let res = client.post("").json(&json_to_send).send().await;
|
||||
match res {
|
||||
Ok(_) => {
|
||||
recived.set("Sent Sucessfully, I will be in contact with you soon".to_string());
|
||||
|
@ -8,8 +8,8 @@
|
||||
//! The [`Navbar`] component will be rendered on all pages of our app since every page is under the layout. The layout defines
|
||||
//! a common wrapper around all child routes.
|
||||
|
||||
mod new_home;
|
||||
pub use new_home::NewHome;
|
||||
// mod new_home;
|
||||
// pub use new_home::NewHome;
|
||||
|
||||
mod blog;
|
||||
pub use blog::{Blog, Blogs};
|
||||
|
@ -1,7 +1,11 @@
|
||||
use crate::components::Ender;
|
||||
use crate::helper_fun::set_meta_tags;
|
||||
use crate::Route;
|
||||
use dioxus::prelude::*;
|
||||
|
||||
const _ROBOTS_TXT: Asset = asset!("/assets/robots.txt");
|
||||
const PROFESSIONAL_PHOTO_JPG: Asset = asset!("assets/professional_photo_2023.jpg");
|
||||
|
||||
/// The Navbar component that will be rendered on all pages of our app since every page is under the layout.
|
||||
///
|
||||
///
|
||||
@ -9,9 +13,43 @@ use dioxus::prelude::*;
|
||||
/// routes will be rendered under the outlet inside this component
|
||||
#[component]
|
||||
pub fn Navbar() -> Element {
|
||||
let PHOTO_FORMAT_URL = format!("https://darkicewolf50.pages.dev{PROFESSIONAL_PHOTO_JPG}");
|
||||
rsx! {
|
||||
document::Stylesheet { href: asset!("/assets/styling/navbar.css") }
|
||||
document::Stylesheet { href: asset!("assets/styling/standardColoursandFonts.css") }
|
||||
set_meta_tags {
|
||||
description: "Fourth year Software Engineering student specializing in full-stack development with a backend focus. Always improving through experience.",
|
||||
keywords: "webdev Rust software engineer projects blog darkicewol50",
|
||||
}
|
||||
document::Meta { name: "robots", content: "index, follow" }
|
||||
document::Link { rel: "canonical", href: "https://darkicewolf50.pages.dev/" }
|
||||
document::Meta {
|
||||
name: "google-site-verification",
|
||||
content: "lsAs9c2Pv7c6Sm26z1hd2YqR2depbp4sJddIDYKHkxY",
|
||||
}
|
||||
document::Meta {
|
||||
property: "og:title",
|
||||
content: "Brock Tomlinson - Software Engineering Student",
|
||||
}
|
||||
document::Meta {
|
||||
property: "og:description",
|
||||
content: "Fourth year Software Engineering student specializing in full-stack development with a backend focus. Always improving through experience.",
|
||||
}
|
||||
document::Meta { property: "og:type", content: "website" }
|
||||
document::Meta { property: "og:url", content: "https://darkicewolf50.pages.dev/" }
|
||||
|
||||
document::Meta { name: "twitter:card", content: "summary_large_image" }
|
||||
document::Meta {
|
||||
name: "twitter:title",
|
||||
content: "Brock Tomlinson - Software Engineer",
|
||||
}
|
||||
document::Meta {
|
||||
name: "twitter:description",
|
||||
content: "Fourth year Software Engineering student specializing in full-stack development with a backend focus. Always improving through experience.",
|
||||
}
|
||||
document::Meta { name: "twitter:image", content: PHOTO_FORMAT_URL.clone() }
|
||||
|
||||
document::Meta { property: "og:image", content: PHOTO_FORMAT_URL.clone() }
|
||||
|
||||
div { id: "navbar",
|
||||
Link { to: Route::Home {}, "Home" }
|
||||
|
@ -13,21 +13,43 @@ pub fn Projects(#[props(default = true)] independent_page: bool) -> Element {
|
||||
}
|
||||
div { class: "project-section",
|
||||
ProjectCards {
|
||||
project_name: "Portfolio Site Version 1.1.0",
|
||||
project_name: "Personal Backend",
|
||||
gitea_link: "https://gitea.bajacloud.duckdns.org/darkicewolf50/darkicewolf50Cloud",
|
||||
dockerhub_link: "https://hub.docker.com/r/darkicewolf50/darkicewolf50cloud",
|
||||
project_img: "https://actix.rs/img/logo.png",
|
||||
techs_used: vec![
|
||||
"Rust",
|
||||
"Actix",
|
||||
"Github Actions",
|
||||
"Docker",
|
||||
"Traefik",
|
||||
"Gitea",
|
||||
"Git",
|
||||
"Github",
|
||||
],
|
||||
project_des: "I find that this is a much better option in compareision to FastAPI as it does not require a post request
|
||||
to input data instead it give the option for the url to give the parameters it needs. I don't have any complains about using Actix, its mature stable and fairly popular.
|
||||
This backend application also uses comrak to convert markdown blogs into html docuemnts that are then seen by you the user.
|
||||
This honestly was a fun challenge getting the blogs previews, ensuring correctness and not allowing for any unknown states,
|
||||
this will serve as a great basis for any future backend requirements that I may have.",
|
||||
}
|
||||
ProjectCards {
|
||||
project_name: "Portfolio Site Version 2.0.1",
|
||||
website_link: "https://darkicewolf50.pages.dev",
|
||||
gitea_link: "https://gitea.bajacloud.duckdns.org/darkicewolf50/personal_site",
|
||||
project_img: "https://res.cloudinary.com/dpgrgsh7g/image/upload/v1745630861/Portfolio_site_k4mhmj.png",
|
||||
techs_used: vec!["Rust", "CSS", "Dioxus", "Git", "Gitea"],
|
||||
project_des: "This project was a great test of my newly learned Rust.
|
||||
This minor update added functionality for the contact me, the ground work for the blogs part of the site, as well as many minor
|
||||
This major update added functionality for the contact me, the blogs part of the site, as well as many minor
|
||||
UI consistencies to ensure that all of the buttons and links felt like buttons and links.
|
||||
I was surprise how easy it was to set up a discord webhook using the 'reqwest' crate.
|
||||
As I continue on I find myself struggling with how and why to use databases for content I generate.
|
||||
I think using tools like disocrd webhooks and email notifications are great for users but certainly not great for reading data from.
|
||||
The part I am both excited for and deading is going to be the blogs component which I believe I have solved for now.",
|
||||
I was very satisfied with serde, and comrak for converting markdown fiels into html.
|
||||
I use this extensively for the blogs search menu and the blog itself to display the blog itself in a consistent way without needing to write a whole library.",
|
||||
}
|
||||
ProjectCards {
|
||||
project_name: "Portfolio Site",
|
||||
project_name: "Portfolio Site 1.0.0",
|
||||
website_link: "https://darkicewolf50.github.io",
|
||||
github_link: "https://github.com/darkicewolf50/darkicewolf50.github.io",
|
||||
project_img: "https://res.cloudinary.com/dpgrgsh7g/image/upload/v1745630861/Portfolio_site_k4mhmj.png",
|
||||
@ -40,6 +62,7 @@ pub fn Projects(#[props(default = true)] independent_page: bool) -> Element {
|
||||
ProjectCards {
|
||||
project_name: "UCalgary Baja Backend",
|
||||
project_img: "https://www.svgrepo.com/show/448221/docker.svg",
|
||||
dockerhub_link: "https://hub.docker.com/r/darkicewolf50/uofcbajacloud",
|
||||
techs_used: vec!["Python", "FastAPI", "Github Actions", "Docker", "Traefik", "Git", "Github"],
|
||||
project_des: "This is going to be extremely cost saving for the non-profit club UCalgary Baja.
|
||||
Using automated uploads and linting to check the Python and FastAPI code was excellent for learning how to self-host a web server.
|
||||
@ -79,6 +102,7 @@ pub fn ProjectCards(
|
||||
website_link: Option<&'static str>,
|
||||
github_link: Option<&'static str>,
|
||||
gitea_link: Option<&'static str>,
|
||||
dockerhub_link: Option<&'static str>,
|
||||
project_name: &'static str,
|
||||
techs_used: Vec<&'static str>,
|
||||
project_des: &'static str,
|
||||
@ -104,6 +128,11 @@ pub fn ProjectCards(
|
||||
get_tech_logos_from_str { used_tech: "Gitea" }
|
||||
}
|
||||
}
|
||||
if let Some(dockerhub) = dockerhub_link {
|
||||
a { href: "{dockerhub}", id: "dockerhub",
|
||||
get_tech_logos_from_str { used_tech: "Docker" }
|
||||
}
|
||||
}
|
||||
if let Some(site) = website_link {
|
||||
a { href: "{site}",
|
||||
get_tech_logos_from_str { used_tech: "Internet" }
|
||||
|
Reference in New Issue
Block a user