Updating components to use the database.
This commit is contained in:
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "bard"
|
name = "bard"
|
||||||
version = "0.0.13"
|
version = "0.0.14"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
description = "Dioxus components that will display a Tavern blogging system Blog."
|
description = "Dioxus components that will display a Tavern blogging system Blog."
|
||||||
repository = "/CyberMages/tavern"
|
repository = "/CyberMages/tavern"
|
||||||
|
|||||||
@ -1,6 +1,4 @@
|
|||||||
|
|
||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
|
|
||||||
use tavern::{Adventurer, Legend, Lore, Tale};
|
use tavern::{Adventurer, Legend, Lore, Tale};
|
||||||
|
|
||||||
use crate::page::Page;
|
use crate::page::Page;
|
||||||
@ -15,51 +13,42 @@ const POST_CSS: Asset = asset!("/assets/css/blog_post.css");
|
|||||||
const TAG_CSS: Asset = asset!("/assets/css/blog_tag.css");
|
const TAG_CSS: Asset = asset!("/assets/css/blog_tag.css");
|
||||||
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq)]
|
|
||||||
pub enum TagStyle
|
fn convert_tag<S>(text: S) -> (String, String)
|
||||||
|
where S: AsRef<str>
|
||||||
{
|
{
|
||||||
Regular,
|
if let Some((first, second)) = text.as_ref().split_once('-')
|
||||||
Easy,
|
{
|
||||||
Medium,
|
(first.to_owned(), second.to_owned())
|
||||||
Hard
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
(text.as_ref().to_owned(), "none".to_owned())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq)]
|
fn strip_tag(text: &str) -> &str
|
||||||
pub enum Tag
|
|
||||||
{
|
{
|
||||||
LeetCode,
|
if let Some((first, _)) = text.split_once('-')
|
||||||
Embedded,
|
{
|
||||||
Simulation,
|
first
|
||||||
Web
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
text
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
pub fn TagItem(tag: Tag, style: TagStyle) -> Element
|
pub fn TagItem(tag: String, style: String) -> Element
|
||||||
{
|
{
|
||||||
let text = match tag
|
rsx! {
|
||||||
{
|
|
||||||
Tag::LeetCode => { "LeetCode" }
|
|
||||||
Tag::Embedded => { "Embedded" }
|
|
||||||
Tag::Simulation => { "Simulation" }
|
|
||||||
Tag::Web => { "Web" }
|
|
||||||
};
|
|
||||||
|
|
||||||
let tag_style = match style
|
|
||||||
{
|
|
||||||
TagStyle::Regular => { "none" }
|
|
||||||
TagStyle::Easy => { "easy" }
|
|
||||||
TagStyle::Medium => { "medium" }
|
|
||||||
TagStyle::Hard => { "hard" }
|
|
||||||
};
|
|
||||||
|
|
||||||
rsx!
|
|
||||||
{
|
|
||||||
li
|
li
|
||||||
{
|
{
|
||||||
class: "tag_item",
|
class: "tag_item",
|
||||||
a { class: "{tag_style}", "{text}" }
|
a { class: "{style}", "{tag}" }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -67,8 +56,7 @@ pub fn TagItem(tag: Tag, style: TagStyle) -> Element
|
|||||||
#[component]
|
#[component]
|
||||||
pub fn TagList(children: Element) -> Element
|
pub fn TagList(children: Element) -> Element
|
||||||
{
|
{
|
||||||
rsx!
|
rsx! {
|
||||||
{
|
|
||||||
document::Link { rel: "stylesheet", href: TAG_CSS }
|
document::Link { rel: "stylesheet", href: TAG_CSS }
|
||||||
|
|
||||||
h5
|
h5
|
||||||
@ -87,8 +75,7 @@ pub fn TagList(children: Element) -> Element
|
|||||||
#[component]
|
#[component]
|
||||||
pub fn BlogAuthor() -> Element
|
pub fn BlogAuthor() -> Element
|
||||||
{
|
{
|
||||||
rsx!
|
rsx! {
|
||||||
{
|
|
||||||
document::Link { rel: "stylesheet", href: AUTHOR_CSS }
|
document::Link { rel: "stylesheet", href: AUTHOR_CSS }
|
||||||
|
|
||||||
section
|
section
|
||||||
@ -101,11 +88,14 @@ pub fn BlogAuthor() -> Element
|
|||||||
|
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
pub fn BlogItem(title: String, slug: String, author: String,
|
pub fn BlogItem(title: String, slug: String, author: String, summary: String,
|
||||||
summary: String, tags: Vec<(Tag, TagStyle)>) -> Element
|
tags: Vec<String>)
|
||||||
|
-> Element
|
||||||
{
|
{
|
||||||
rsx!
|
let tags: Vec<(String, String)> =
|
||||||
{
|
tags.iter().map(|t| convert_tag(t)).collect();
|
||||||
|
|
||||||
|
rsx! {
|
||||||
li
|
li
|
||||||
{
|
{
|
||||||
class: "blog_item",
|
class: "blog_item",
|
||||||
@ -120,8 +110,8 @@ pub fn BlogItem(title: String, slug: String, author: String,
|
|||||||
{
|
{
|
||||||
TagItem
|
TagItem
|
||||||
{
|
{
|
||||||
tag: tag,
|
tag: tag.clone(),
|
||||||
style: style
|
style: style.clone()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -135,17 +125,12 @@ pub fn BlogItem(title: String, slug: String, author: String,
|
|||||||
#[component]
|
#[component]
|
||||||
pub fn BlogList(tags: Vec<String>, children: Element) -> Element
|
pub fn BlogList(tags: Vec<String>, children: Element) -> Element
|
||||||
{
|
{
|
||||||
let summaries = use_server_future(move ||
|
let summaries = use_server_future(move || {
|
||||||
{
|
|
||||||
let categories = tags.clone();
|
let categories = tags.clone();
|
||||||
async move
|
async move { get_blog_list(categories).await }
|
||||||
{
|
|
||||||
get_blog_list(categories).await
|
|
||||||
}
|
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
rsx!
|
rsx! {
|
||||||
{
|
|
||||||
document::Link { rel: "stylesheet", href: LIST_CSS }
|
document::Link { rel: "stylesheet", href: LIST_CSS }
|
||||||
|
|
||||||
section
|
section
|
||||||
@ -155,6 +140,44 @@ pub fn BlogList(tags: Vec<String>, children: Element) -> Element
|
|||||||
{
|
{
|
||||||
class: "blog_list",
|
class: "blog_list",
|
||||||
|
|
||||||
|
match &*summaries.read()
|
||||||
|
{
|
||||||
|
Some(Ok(lores)) =>
|
||||||
|
{
|
||||||
|
rsx!
|
||||||
|
{
|
||||||
|
for lore in lores
|
||||||
|
{
|
||||||
|
BlogItem
|
||||||
|
{
|
||||||
|
title: lore.title.clone(),
|
||||||
|
slug: lore.slug.clone(),
|
||||||
|
author: lore.author.clone(),
|
||||||
|
summary: lore.summary.clone(),
|
||||||
|
tags: lore.tags.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(Err(e)) =>
|
||||||
|
{
|
||||||
|
rsx!
|
||||||
|
{
|
||||||
|
p { "Unable to show post header." }
|
||||||
|
p { "{e}" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None =>
|
||||||
|
{
|
||||||
|
rsx!
|
||||||
|
{
|
||||||
|
p { "Loading..." }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
{children}
|
{children}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -164,16 +187,9 @@ pub fn BlogList(tags: Vec<String>, children: Element) -> Element
|
|||||||
#[component]
|
#[component]
|
||||||
pub fn BlogNav() -> Element
|
pub fn BlogNav() -> Element
|
||||||
{
|
{
|
||||||
let tags = use_server_future(move ||
|
let tags = use_server_future(move || async move { get_tags().await })?;
|
||||||
{
|
|
||||||
async move
|
|
||||||
{
|
|
||||||
get_tags().await
|
|
||||||
}
|
|
||||||
})?;
|
|
||||||
|
|
||||||
rsx!
|
rsx! {
|
||||||
{
|
|
||||||
document::Link { rel: "stylesheet", href: NAV_CSS }
|
document::Link { rel: "stylesheet", href: NAV_CSS }
|
||||||
|
|
||||||
section
|
section
|
||||||
@ -186,25 +202,39 @@ pub fn BlogNav() -> Element
|
|||||||
ul
|
ul
|
||||||
{
|
{
|
||||||
class: "tag_list",
|
class: "tag_list",
|
||||||
|
match &*tags.read()
|
||||||
|
{
|
||||||
|
Some(Ok(categories)) =>
|
||||||
|
{
|
||||||
|
rsx!
|
||||||
|
{
|
||||||
|
for tag in categories
|
||||||
|
{
|
||||||
li
|
li
|
||||||
{
|
{
|
||||||
class: "tag_item",
|
class: "tag_item",
|
||||||
a { href: "/blog/LeetCode", "LeetCode" }
|
a { href: "/blog/{strip_tag(tag)}", "{strip_tag(tag)}" }
|
||||||
}
|
}
|
||||||
li
|
|
||||||
{
|
|
||||||
class: "tag_item",
|
|
||||||
a { href: "/blog/Embedded", "Embedded" }
|
|
||||||
}
|
}
|
||||||
li
|
|
||||||
{
|
|
||||||
class: "tag_item",
|
|
||||||
a { href: "/blog/Simulation", "Simulation" }
|
|
||||||
}
|
}
|
||||||
li
|
}
|
||||||
|
|
||||||
|
Some(Err(e)) =>
|
||||||
{
|
{
|
||||||
class: "tag_item",
|
rsx!
|
||||||
a { href: "/blog/Web", "Web" }
|
{
|
||||||
|
p { "Unable to show desired post." }
|
||||||
|
p { "{e}" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None =>
|
||||||
|
{
|
||||||
|
rsx!
|
||||||
|
{
|
||||||
|
p { "Loading..." }
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -214,10 +244,10 @@ pub fn BlogNav() -> Element
|
|||||||
|
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
pub fn PostHeaderAuthor(name: String, handle: String, profile_link: String) -> Element
|
pub fn PostHeaderAuthor(name: String, handle: String, profile_link: String)
|
||||||
|
-> Element
|
||||||
{
|
{
|
||||||
rsx!
|
rsx! {
|
||||||
{
|
|
||||||
h4 { "Author: ", a { href: "{profile_link}", "{name} @{handle}" } }
|
h4 { "Author: ", a { href: "{profile_link}", "{name} @{handle}" } }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -225,27 +255,25 @@ pub fn PostHeaderAuthor(name: String, handle: String, profile_link: String) -> E
|
|||||||
#[component]
|
#[component]
|
||||||
pub fn PostHeader(title: String, author: String, tags: Vec<String>) -> Element
|
pub fn PostHeader(title: String, author: String, tags: Vec<String>) -> Element
|
||||||
{
|
{
|
||||||
let author = use_server_future(move ||
|
let author = use_server_future(move || {
|
||||||
{
|
|
||||||
let target_author = author.clone();
|
let target_author = author.clone();
|
||||||
async move
|
async move { get_author(target_author).await }
|
||||||
{
|
|
||||||
get_author(target_author).await
|
|
||||||
}
|
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
|
||||||
rsx!
|
let converted_tags: Vec<(String, String)> =
|
||||||
{
|
tags.iter().map(|t| convert_tag(t)).collect();
|
||||||
|
|
||||||
|
rsx! {
|
||||||
h1 { "{title}" }
|
h1 { "{title}" }
|
||||||
TagList
|
TagList
|
||||||
{
|
{
|
||||||
for category in tags
|
for (tag, style) in converted_tags
|
||||||
{
|
{
|
||||||
TagItem
|
TagItem
|
||||||
{
|
{
|
||||||
tag: Tag::LeetCode,
|
tag: tag.clone(),
|
||||||
style: TagStyle::Regular
|
style: style.clone()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -288,17 +316,12 @@ pub fn PostHeader(title: String, author: String, tags: Vec<String>) -> Element
|
|||||||
#[component]
|
#[component]
|
||||||
pub fn BlogPost(slug: String, children: Element) -> Element
|
pub fn BlogPost(slug: String, children: Element) -> Element
|
||||||
{
|
{
|
||||||
let post = use_server_future(move ||
|
let post = use_server_future(move || {
|
||||||
{
|
|
||||||
let url_slug = slug.clone();
|
let url_slug = slug.clone();
|
||||||
async move
|
async move { get_blog_post(url_slug).await }
|
||||||
{
|
|
||||||
get_blog_post(url_slug).await
|
|
||||||
}
|
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
rsx!
|
rsx! {
|
||||||
{
|
|
||||||
document::Link { rel: "stylesheet", href: POST_CSS }
|
document::Link { rel: "stylesheet", href: POST_CSS }
|
||||||
|
|
||||||
article
|
article
|
||||||
|
|||||||
@ -9,11 +9,9 @@ mod server;
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
pub use crate::info::{get_name, get_version};
|
|
||||||
|
|
||||||
pub use crate::components::*;
|
pub use crate::components::*;
|
||||||
|
pub use crate::info::{get_name, get_version};
|
||||||
pub use crate::page::Page;
|
pub use crate::page::Page;
|
||||||
pub use crate::pages::*;
|
pub use crate::pages::*;
|
||||||
|
|
||||||
#[cfg(feature = "server")]
|
#[cfg(feature = "server")]
|
||||||
pub use crate::server::*;
|
pub use crate::server::*;
|
||||||
|
|||||||
@ -1,8 +1,7 @@
|
|||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
|
|
||||||
use crate::page::Page;
|
|
||||||
|
|
||||||
use crate::components::BlogList;
|
use crate::components::BlogList;
|
||||||
|
use crate::page::Page;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -21,8 +20,7 @@ pub fn Blog(tag: String) -> Element
|
|||||||
categories.push(tag);
|
categories.push(tag);
|
||||||
}
|
}
|
||||||
|
|
||||||
rsx!
|
rsx! {
|
||||||
{
|
|
||||||
document::Stylesheet { href: BLOG_CSS }
|
document::Stylesheet { href: BLOG_CSS }
|
||||||
|
|
||||||
main
|
main
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
|
|
||||||
use crate::page::Page;
|
|
||||||
use crate::components::BlogPost;
|
use crate::components::BlogPost;
|
||||||
|
use crate::page::Page;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -13,8 +13,7 @@ const BLOG_CSS: Asset = asset!("/assets/css/blog.css");
|
|||||||
#[component]
|
#[component]
|
||||||
pub fn Post(slug: String) -> Element
|
pub fn Post(slug: String) -> Element
|
||||||
{
|
{
|
||||||
rsx!
|
rsx! {
|
||||||
{
|
|
||||||
document::Stylesheet { href: BLOG_CSS }
|
document::Stylesheet { href: BLOG_CSS }
|
||||||
|
|
||||||
main
|
main
|
||||||
|
|||||||
@ -3,21 +3,19 @@
|
|||||||
//
|
//
|
||||||
// Server functions must be:
|
// Server functions must be:
|
||||||
// * Be an async function
|
// * Be an async function
|
||||||
// * Have arguments and a return type that both implement serialize and deserialize (with serde).
|
// * Have arguments and a return type that both implement serialize and
|
||||||
|
// deserialize (with serde).
|
||||||
// * Return a Result with an error type of ServerFnError
|
// * Return a Result with an error type of ServerFnError
|
||||||
|
|
||||||
#[cfg(feature = "server")]
|
#[cfg(feature = "server")]
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
|
|
||||||
#[cfg(feature = "server")]
|
|
||||||
use tokio::sync::OnceCell;
|
|
||||||
|
|
||||||
use tavern::{Adventurer, Lore, Tale};
|
|
||||||
|
|
||||||
#[cfg(feature = "server")]
|
#[cfg(feature = "server")]
|
||||||
use tavern::Database;
|
use tavern::Database;
|
||||||
|
use tavern::{Adventurer, Lore, Tale};
|
||||||
|
#[cfg(feature = "server")]
|
||||||
|
use tokio::sync::OnceCell;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -27,7 +25,9 @@ static BLOG_DATABASE: OnceCell<Arc<Database>> = OnceCell::const_new();
|
|||||||
|
|
||||||
|
|
||||||
#[cfg(feature = "server")]
|
#[cfg(feature = "server")]
|
||||||
async fn get_database_instance<P>(path: P) -> Result<&'static Arc<Database>, ServerFnError>
|
async fn get_database_instance<P>(
|
||||||
|
path: P)
|
||||||
|
-> Result<&'static Arc<Database>, ServerFnError>
|
||||||
where P: AsRef<std::path::Path>
|
where P: AsRef<std::path::Path>
|
||||||
{
|
{
|
||||||
BLOG_DATABASE.get_or_try_init(|| async
|
BLOG_DATABASE.get_or_try_init(|| async
|
||||||
@ -45,8 +45,8 @@ pub async fn init_database<P>(path: P) -> Result<(), ServerFnError>
|
|||||||
{
|
{
|
||||||
match get_database_instance(path).await
|
match get_database_instance(path).await
|
||||||
{
|
{
|
||||||
Ok(_) => { Ok(()) }
|
Ok(_) => Ok(()),
|
||||||
Err(e) => { Err(e) }
|
Err(e) => Err(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,11 +69,13 @@ pub async fn get_tags() -> Result<Vec<String>, ServerFnError>
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[server]
|
#[server]
|
||||||
pub async fn get_blog_list(categories: Vec<String>) -> Result<Vec<Lore>, ServerFnError>
|
pub async fn get_blog_list(categories: Vec<String>)
|
||||||
|
-> Result<Vec<Lore>, ServerFnError>
|
||||||
{
|
{
|
||||||
let db = get_database().await?;
|
let db = get_database().await?;
|
||||||
|
|
||||||
let summaries = db.get_tales_summary(&categories).await
|
let summaries = db.get_tales_summary(&categories)
|
||||||
|
.await
|
||||||
.map_err(|e| ServerFnError::new(e))?;
|
.map_err(|e| ServerFnError::new(e))?;
|
||||||
|
|
||||||
Ok(summaries)
|
Ok(summaries)
|
||||||
@ -85,7 +87,8 @@ pub async fn get_blog_post(slug: String) -> Result<Tale, ServerFnError>
|
|||||||
{
|
{
|
||||||
let db = get_database().await?;
|
let db = get_database().await?;
|
||||||
|
|
||||||
let tale = db.get_tale_by_slug(&slug).await
|
let tale = db.get_tale_by_slug(&slug)
|
||||||
|
.await
|
||||||
.map_err(|e| ServerFnError::new(e))?;
|
.map_err(|e| ServerFnError::new(e))?;
|
||||||
|
|
||||||
tale.ok_or(ServerFnError::new(format!("Post {} not found", slug)))
|
tale.ok_or(ServerFnError::new(format!("Post {} not found", slug)))
|
||||||
@ -97,7 +100,8 @@ pub async fn get_author(handle: String) -> Result<Adventurer, ServerFnError>
|
|||||||
{
|
{
|
||||||
let db = get_database().await?;
|
let db = get_database().await?;
|
||||||
|
|
||||||
let author = db.get_adventurer(&handle).await
|
let author = db.get_adventurer(&handle)
|
||||||
|
.await
|
||||||
.map_err(|e| ServerFnError::new(e))?;
|
.map_err(|e| ServerFnError::new(e))?;
|
||||||
|
|
||||||
author.ok_or(ServerFnError::new(format!("Author {} not found.", handle)))
|
author.ok_or(ServerFnError::new(format!("Author {} not found.", handle)))
|
||||||
|
|||||||
Reference in New Issue
Block a user