Adding components and assets.
Commiting to try the library with the target dioxus project.
This commit is contained in:
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "bard"
|
name = "bard"
|
||||||
version = "0.0.2"
|
version = "0.0.3"
|
||||||
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"
|
||||||
@ -10,8 +10,10 @@ readme = "README.md"
|
|||||||
license = "Apache-2.0"
|
license = "Apache-2.0"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
dioxus = { version = "*", features = ["router", "fullstack"], optional = true }
|
||||||
tavern = { version = "0.2.4", path = "../tavern", registry = "cybermages", optional = true}
|
tavern = { version = "0.2.4", path = "../tavern", registry = "cybermages", optional = true}
|
||||||
|
tokio = { version = "1.0", features = ["full"], optional = true }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["tavern"]
|
default = ["tavern", "dioxus/web"]
|
||||||
server = ["tavern/database"]
|
server = ["tavern/database", "dioxus/server", "tokio"]
|
||||||
|
|||||||
19
bard/assets/css/blog.css
Normal file
19
bard/assets/css/blog.css
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
.blog_style
|
||||||
|
{
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: var(--bg-color);
|
||||||
|
padding: 50px 0px;
|
||||||
|
justify-content: center;
|
||||||
|
container-name: site;
|
||||||
|
container-type: inline-size;
|
||||||
|
|
||||||
|
.page_content
|
||||||
|
{
|
||||||
|
display: flex;
|
||||||
|
width: 67%;
|
||||||
|
container-name: page;
|
||||||
|
container-type: inline-size;
|
||||||
|
}
|
||||||
|
}
|
||||||
230
bard/assets/css/blog_author.css
Normal file
230
bard/assets/css/blog_author.css
Normal file
@ -0,0 +1,230 @@
|
|||||||
|
.blog_style
|
||||||
|
{
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: var(--bg-color);
|
||||||
|
padding: 50px 0px;
|
||||||
|
justify-content: center;
|
||||||
|
container-name: site;
|
||||||
|
container-type: inline-size;
|
||||||
|
|
||||||
|
.page_content
|
||||||
|
{
|
||||||
|
display: flex;
|
||||||
|
width: 67%;
|
||||||
|
container-name: page;
|
||||||
|
container-type: inline-size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.blog_nav
|
||||||
|
{
|
||||||
|
width: 15%;
|
||||||
|
padding: 0px 20px;
|
||||||
|
|
||||||
|
h3
|
||||||
|
{
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul
|
||||||
|
{
|
||||||
|
list-style: none;
|
||||||
|
|
||||||
|
li
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@container site (max-width: 1230px)
|
||||||
|
{
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.social
|
||||||
|
{
|
||||||
|
/* Hide .social until the font is loaded */
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 15px;
|
||||||
|
margin: 25px 0;
|
||||||
|
font-family: cm_social;
|
||||||
|
|
||||||
|
a
|
||||||
|
{
|
||||||
|
color: var(--text-color);
|
||||||
|
font-size: 46px;
|
||||||
|
text-decoration: none;
|
||||||
|
background: transparent;
|
||||||
|
width: 65px;
|
||||||
|
height: 65px;
|
||||||
|
border-radius: 50%;
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-end;
|
||||||
|
justify-content: center;
|
||||||
|
transition: 0.4s ease;
|
||||||
|
border: 4px solid var(--text-color);
|
||||||
|
|
||||||
|
&:hover
|
||||||
|
{
|
||||||
|
color: var(--accent-color);
|
||||||
|
border: 4px solid var(--accent-color);
|
||||||
|
transform: translateY(-7px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.blog_item_area
|
||||||
|
{
|
||||||
|
.blog_list
|
||||||
|
{
|
||||||
|
list-style: none;
|
||||||
|
|
||||||
|
.blog_item
|
||||||
|
{
|
||||||
|
margin-bottom: 50px;
|
||||||
|
|
||||||
|
h1
|
||||||
|
{
|
||||||
|
color: var(--accent-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
h4
|
||||||
|
{
|
||||||
|
margin: 10px 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
p
|
||||||
|
{
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.blog_article
|
||||||
|
{
|
||||||
|
h1
|
||||||
|
{
|
||||||
|
color: var(--accent-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
h4
|
||||||
|
{
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
p
|
||||||
|
{
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.embeded_video
|
||||||
|
{
|
||||||
|
margin-top: 50px;
|
||||||
|
display: flex;
|
||||||
|
align-content: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
iframe
|
||||||
|
{
|
||||||
|
box-shadow: 0 0 80px var(--accent-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.tag_style
|
||||||
|
{
|
||||||
|
display: block;
|
||||||
|
|
||||||
|
.tag_list
|
||||||
|
{
|
||||||
|
list-style: none;
|
||||||
|
|
||||||
|
.tag_item
|
||||||
|
{
|
||||||
|
display: inline-block;
|
||||||
|
margin: 5px 5px 0px 0px;
|
||||||
|
|
||||||
|
a
|
||||||
|
{
|
||||||
|
display: block;
|
||||||
|
padding: 0px 4px;
|
||||||
|
text-decoration: none;
|
||||||
|
border: 2px solid var(--text-color);
|
||||||
|
border-radius: 5px;
|
||||||
|
|
||||||
|
color: var(--text-color);
|
||||||
|
transition: 0.1s ease-in;
|
||||||
|
|
||||||
|
&:hover
|
||||||
|
{
|
||||||
|
color: var(--accent-color);
|
||||||
|
border: 2px solid var(--accent-color);
|
||||||
|
transform: translateY(-2px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.easy
|
||||||
|
{
|
||||||
|
background-color: #248721;
|
||||||
|
}
|
||||||
|
|
||||||
|
.medium
|
||||||
|
{
|
||||||
|
background-color: #d6a318;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hard
|
||||||
|
{
|
||||||
|
background-color: #d92121;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.social
|
||||||
|
{
|
||||||
|
background: transparent;
|
||||||
|
font-family: cm_social;
|
||||||
|
margin: 15px 0px;
|
||||||
|
|
||||||
|
a
|
||||||
|
{
|
||||||
|
color: var(--text-color);
|
||||||
|
font-size: 30px;
|
||||||
|
text-decoration: none;
|
||||||
|
background: transparent;
|
||||||
|
width: 45px;
|
||||||
|
height: 45px;
|
||||||
|
border-radius: 50%;
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-end;
|
||||||
|
justify-content: center;
|
||||||
|
transition: 0.4s ease;
|
||||||
|
border: 4px solid var(--text-color);
|
||||||
|
|
||||||
|
&:hover
|
||||||
|
{
|
||||||
|
color: var(--accent-color);
|
||||||
|
border: 4px solid var(--accent-color);
|
||||||
|
transform: translateY(-7px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
27
bard/assets/css/blog_list.css
Normal file
27
bard/assets/css/blog_list.css
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
.blog_list_style
|
||||||
|
{
|
||||||
|
.blog_list
|
||||||
|
{
|
||||||
|
list-style: none;
|
||||||
|
|
||||||
|
.blog_item
|
||||||
|
{
|
||||||
|
margin-bottom: 50px;
|
||||||
|
|
||||||
|
h1
|
||||||
|
{
|
||||||
|
color: var(--accent-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
h4
|
||||||
|
{
|
||||||
|
margin: 10px 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
p
|
||||||
|
{
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
116
bard/assets/css/blog_nav.css
Normal file
116
bard/assets/css/blog_nav.css
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
.blog_style
|
||||||
|
{
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: var(--bg-color);
|
||||||
|
padding: 50px 0px;
|
||||||
|
justify-content: center;
|
||||||
|
container-name: site;
|
||||||
|
container-type: inline-size;
|
||||||
|
|
||||||
|
.page_content
|
||||||
|
{
|
||||||
|
display: flex;
|
||||||
|
width: 67%;
|
||||||
|
container-name: page;
|
||||||
|
container-type: inline-size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.blog_nav_style
|
||||||
|
{
|
||||||
|
width: 15%;
|
||||||
|
padding: 0px 20px;
|
||||||
|
|
||||||
|
h3
|
||||||
|
{
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul
|
||||||
|
{
|
||||||
|
list-style: none;
|
||||||
|
|
||||||
|
li
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@container site (max-width: 1230px)
|
||||||
|
{
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.social
|
||||||
|
{
|
||||||
|
/* Hide .social until the font is loaded */
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 15px;
|
||||||
|
margin: 25px 0;
|
||||||
|
font-family: cm_social;
|
||||||
|
|
||||||
|
a
|
||||||
|
{
|
||||||
|
color: var(--text-color);
|
||||||
|
font-size: 22px;
|
||||||
|
text-decoration: none;
|
||||||
|
background: transparent;
|
||||||
|
width: 35px;
|
||||||
|
height: 35px;
|
||||||
|
border-radius: 50%;
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-end;
|
||||||
|
justify-content: center;
|
||||||
|
transition: 0.4s ease;
|
||||||
|
border: 4px solid var(--text-color);
|
||||||
|
|
||||||
|
&:hover
|
||||||
|
{
|
||||||
|
color: var(--accent-color);
|
||||||
|
border: 4px solid var(--accent-color);
|
||||||
|
transform: translateY(-7px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tag_style
|
||||||
|
{
|
||||||
|
display: block;
|
||||||
|
|
||||||
|
.tag_list
|
||||||
|
{
|
||||||
|
display: inline-flex;
|
||||||
|
flex-direction: column;
|
||||||
|
list-style: none;
|
||||||
|
|
||||||
|
.tag_item
|
||||||
|
{
|
||||||
|
display: inline-block;
|
||||||
|
margin: 5px 5px 0px 0px;
|
||||||
|
|
||||||
|
a
|
||||||
|
{
|
||||||
|
display: inline-flex;
|
||||||
|
padding: 0px 4px;
|
||||||
|
text-decoration: none;
|
||||||
|
border: 2px solid var(--text-color);
|
||||||
|
border-radius: 5px;
|
||||||
|
|
||||||
|
color: var(--text-color);
|
||||||
|
transition: 0.1s ease-in;
|
||||||
|
|
||||||
|
&:hover
|
||||||
|
{
|
||||||
|
color: var(--accent-color);
|
||||||
|
border: 2px solid var(--accent-color);
|
||||||
|
transform: translateY(-2px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
117
bard/assets/css/blog_post.css
Normal file
117
bard/assets/css/blog_post.css
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
.blog_post_style
|
||||||
|
{
|
||||||
|
h1
|
||||||
|
{
|
||||||
|
color: var(--accent-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
h4
|
||||||
|
{
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
p
|
||||||
|
{
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.embeded_video
|
||||||
|
{
|
||||||
|
margin-top: 50px;
|
||||||
|
display: flex;
|
||||||
|
align-content: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
iframe
|
||||||
|
{
|
||||||
|
box-shadow: 0 0 80px var(--accent-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.tag_style
|
||||||
|
{
|
||||||
|
display: block;
|
||||||
|
|
||||||
|
.tag_list
|
||||||
|
{
|
||||||
|
list-style: none;
|
||||||
|
|
||||||
|
.tag_item
|
||||||
|
{
|
||||||
|
display: inline-block;
|
||||||
|
margin: 5px 5px 0px 0px;
|
||||||
|
|
||||||
|
a
|
||||||
|
{
|
||||||
|
display: block;
|
||||||
|
padding: 0px 4px;
|
||||||
|
text-decoration: none;
|
||||||
|
border: 2px solid var(--text-color);
|
||||||
|
border-radius: 5px;
|
||||||
|
|
||||||
|
color: var(--text-color);
|
||||||
|
transition: 0.1s ease-in;
|
||||||
|
|
||||||
|
&:hover
|
||||||
|
{
|
||||||
|
color: var(--accent-color);
|
||||||
|
border: 2px solid var(--accent-color);
|
||||||
|
transform: translateY(-2px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.easy
|
||||||
|
{
|
||||||
|
background-color: #248721;
|
||||||
|
}
|
||||||
|
|
||||||
|
.medium
|
||||||
|
{
|
||||||
|
background-color: #d6a318;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hard
|
||||||
|
{
|
||||||
|
background-color: #d92121;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.social
|
||||||
|
{
|
||||||
|
background: transparent;
|
||||||
|
font-family: cm_social;
|
||||||
|
margin: 15px 0px;
|
||||||
|
|
||||||
|
a
|
||||||
|
{
|
||||||
|
color: var(--text-color);
|
||||||
|
font-size: 30px;
|
||||||
|
text-decoration: none;
|
||||||
|
background: transparent;
|
||||||
|
width: 45px;
|
||||||
|
height: 45px;
|
||||||
|
border-radius: 50%;
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-end;
|
||||||
|
justify-content: center;
|
||||||
|
transition: 0.4s ease;
|
||||||
|
border: 4px solid var(--text-color);
|
||||||
|
|
||||||
|
&:hover
|
||||||
|
{
|
||||||
|
color: var(--accent-color);
|
||||||
|
border: 4px solid var(--accent-color);
|
||||||
|
transform: translateY(-7px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
54
bard/assets/css/blog_tag.css
Normal file
54
bard/assets/css/blog_tag.css
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
.blog_tag_style
|
||||||
|
{
|
||||||
|
display: block;
|
||||||
|
|
||||||
|
.tag_list
|
||||||
|
{
|
||||||
|
list-style: none;
|
||||||
|
|
||||||
|
.tag_item
|
||||||
|
{
|
||||||
|
display: inline-block;
|
||||||
|
margin: 5px 5px 0px 0px;
|
||||||
|
|
||||||
|
a
|
||||||
|
{
|
||||||
|
display: block;
|
||||||
|
padding: 0px 4px;
|
||||||
|
text-decoration: none;
|
||||||
|
border: 2px solid var(--text-color);
|
||||||
|
border-radius: 5px;
|
||||||
|
|
||||||
|
color: var(--text-color);
|
||||||
|
transition: 0.1s ease-in;
|
||||||
|
|
||||||
|
&:hover
|
||||||
|
{
|
||||||
|
color: var(--accent-color);
|
||||||
|
border: 2px solid var(--accent-color);
|
||||||
|
transform: translateY(-2px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.none
|
||||||
|
{
|
||||||
|
background-color: var(--bg-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.easy
|
||||||
|
{
|
||||||
|
background-color: #248721;
|
||||||
|
}
|
||||||
|
|
||||||
|
.medium
|
||||||
|
{
|
||||||
|
background-color: #d6a318;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hard
|
||||||
|
{
|
||||||
|
background-color: #d92121;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,10 +1,9 @@
|
|||||||
|
|
||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
|
|
||||||
use tavern::Lore;
|
use tavern::{Adventurer, Legend, Lore, Tale};
|
||||||
|
|
||||||
use crate::components::social::*;
|
use crate::server::*;
|
||||||
use crate::page::Page;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -15,7 +14,6 @@ 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)]
|
#[derive(Clone, Copy, PartialEq)]
|
||||||
pub enum TagStyle
|
pub enum TagStyle
|
||||||
{
|
{
|
||||||
@ -85,6 +83,7 @@ pub fn TagList(children: Element) -> Element
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
#[component]
|
#[component]
|
||||||
pub fn BlogAuthor() -> Element
|
pub fn BlogAuthor() -> Element
|
||||||
{
|
{
|
||||||
@ -210,20 +209,32 @@ pub fn BlogNav() -> Element
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
pub fn BlogPost(children: Element) -> Element
|
pub fn PostHeaderAuthor(name: String, handle: String, profile_link: String) -> Element
|
||||||
{
|
{
|
||||||
rsx!
|
rsx!
|
||||||
{
|
{
|
||||||
document::Link { rel: "stylesheet", href: POST_CSS }
|
h4 { "Author: ", a { href: "{profile_link}", "{name} @{handle}" } }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
article
|
#[component]
|
||||||
|
pub fn PostHeader(title: String, author: String, tags: Vec<String>) -> Element
|
||||||
{
|
{
|
||||||
class: "blog_post_style",
|
let Ok(author) = use_server_future(move || get_author(author.clone())) else
|
||||||
h1 { "Leet Code: Let's Get Started" }
|
{
|
||||||
|
return rsx! { p { "Failed to load author." } }
|
||||||
|
};
|
||||||
|
|
||||||
|
rsx!
|
||||||
|
{
|
||||||
|
h1 { "{title}" }
|
||||||
TagList
|
TagList
|
||||||
|
{
|
||||||
|
for category in tags
|
||||||
{
|
{
|
||||||
TagItem
|
TagItem
|
||||||
{
|
{
|
||||||
@ -231,7 +242,96 @@ pub fn BlogPost(children: Element) -> Element
|
|||||||
style: TagStyle::Regular
|
style: TagStyle::Regular
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
h4 { "Author: ", a { href: "https://cybermages.tech", "Jason Smith" } }
|
}
|
||||||
|
|
||||||
|
match &*author.read()
|
||||||
|
{
|
||||||
|
Some(Ok(author)) =>
|
||||||
|
{
|
||||||
|
rsx!
|
||||||
|
{
|
||||||
|
PostHeaderAuthor
|
||||||
|
{
|
||||||
|
name: author.name.clone(),
|
||||||
|
handle: author.handle.clone(),
|
||||||
|
profile_link: author.legend.profile.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(Err(e)) =>
|
||||||
|
{
|
||||||
|
rsx!
|
||||||
|
{
|
||||||
|
p { "Unable to show post header." }
|
||||||
|
p { "{e}" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None =>
|
||||||
|
{
|
||||||
|
rsx!
|
||||||
|
{
|
||||||
|
p { "Loading..." }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
pub fn BlogPost(slug: String, children: Element) -> Element
|
||||||
|
{
|
||||||
|
// 1. Fetch the blog post using the slug.
|
||||||
|
let Ok(post) = use_server_future(move || get_blog_post(slug.clone())) else
|
||||||
|
{
|
||||||
|
return rsx! { p { "Failed to load post." } }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Then build the component.
|
||||||
|
rsx!
|
||||||
|
{
|
||||||
|
document::Link { rel: "stylesheet", href: POST_CSS }
|
||||||
|
|
||||||
|
article
|
||||||
|
{
|
||||||
|
class: "blog_post_style",
|
||||||
|
|
||||||
|
match &*post.read()
|
||||||
|
{
|
||||||
|
Some(Ok(post)) =>
|
||||||
|
{
|
||||||
|
rsx!
|
||||||
|
{
|
||||||
|
PostHeader
|
||||||
|
{
|
||||||
|
title: post.lore.title.clone(),
|
||||||
|
author: post.lore.author.clone(),
|
||||||
|
tags: post.lore.tags.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
div { dangerous_inner_html: "{post.story}" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(Err(e)) =>
|
||||||
|
{
|
||||||
|
rsx!
|
||||||
|
{
|
||||||
|
p { "Unable to show desired post." }
|
||||||
|
p { "{e}" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None =>
|
||||||
|
{
|
||||||
|
rsx!
|
||||||
|
{
|
||||||
|
p { "Loading..." }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
{children}
|
{children}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,20 +2,17 @@
|
|||||||
|
|
||||||
mod info;
|
mod info;
|
||||||
|
|
||||||
|
#[cfg(not(feature = "server"))]
|
||||||
|
mod components;
|
||||||
|
|
||||||
|
mod server;
|
||||||
|
|
||||||
|
|
||||||
#[cfg(feature = "server")]
|
|
||||||
use tavern::Database;
|
|
||||||
|
|
||||||
pub use crate::info::{get_name, get_version};
|
pub use crate::info::{get_name, get_version};
|
||||||
|
|
||||||
|
#[cfg(not(feature = "server"))]
|
||||||
|
pub use crate::components::*;
|
||||||
|
|
||||||
#[cfg(feature = "server")]
|
#[cfg(feature = "server")]
|
||||||
pub async fn init_database<P>(path: P)
|
pub use crate::server::*;
|
||||||
-> Result<std::sync::Arc<Database>, Box<dyn std::error::Error>>
|
|
||||||
where P: AsRef<std::path::Path>
|
|
||||||
{
|
|
||||||
let db = Database::open(path).await?;
|
|
||||||
Ok(std::sync::Arc::new(db))
|
|
||||||
}
|
|
||||||
|
|||||||
@ -1,27 +1,70 @@
|
|||||||
use dioxus::prelude::*;
|
// Remember that Server functions parameters must not be references.
|
||||||
|
// They are coming from potentially other computers.
|
||||||
|
|
||||||
|
#[cfg(feature = "server")]
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use crate::{Database, Lore, Tale};
|
|
||||||
|
use dioxus::prelude::*;
|
||||||
|
|
||||||
|
#[cfg(feature = "server")]
|
||||||
|
use tokio::sync::OnceCell;
|
||||||
|
|
||||||
|
use tavern::{Adventurer, Lore, Tale};
|
||||||
|
|
||||||
|
#[cfg(feature = "server")]
|
||||||
|
use tavern::Database;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#[cfg(feature = "server")]
|
||||||
|
static BLOG_DATABASE: OnceCell<Arc<Database>> = OnceCell::const_new();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#[cfg(feature = "server")]
|
||||||
|
async fn get_database_instance() -> &'static Arc<Database>
|
||||||
|
{
|
||||||
|
BLOG_DATABASE.get_or_init(|| async {
|
||||||
|
let db = Database::open("tavern.db")
|
||||||
|
.await
|
||||||
|
.expect("Failed to open database");
|
||||||
|
Arc::new(db)
|
||||||
|
}).await
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#[server]
|
#[server]
|
||||||
pub async fn get_blog_list() -> Result<Vec<Lore>, ServerFnError> {
|
pub async fn get_blog_list(categories: Vec<String>) -> Result<Vec<Lore>, ServerFnError>
|
||||||
let db = server_context()
|
{
|
||||||
.get::<Arc<Database>>()
|
let db = get_database_instance().await;
|
||||||
.ok_or_else(|| ServerFnError::ServerError("Database context not available".to_string()))?;
|
|
||||||
|
|
||||||
let summaries = db.get_tales_summary(&[]).await
|
let summaries = db.get_tales_summary(&categories).await
|
||||||
.map_err(|e| ServerFnError::ServerError(e.to_string()))?;
|
.map_err(|e| ServerFnError::new(e))?;
|
||||||
|
|
||||||
Ok(summaries)
|
Ok(summaries)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[server]
|
#[server]
|
||||||
pub async fn get_blog_post(slug: String) -> Result<Tale, ServerFnError> {
|
pub async fn get_blog_post(slug: String) -> Result<Tale, ServerFnError>
|
||||||
let db = server_context()
|
{
|
||||||
.get::<Arc<Database>>()
|
let db = get_database_instance().await;
|
||||||
.ok_or_else(|| ServerFnError::ServerError("Database context not available".to_string()))?;
|
|
||||||
|
|
||||||
let tale = db.get_tale_by_slug(&slug).await
|
let tale = db.get_tale_by_slug(&slug).await
|
||||||
.map_err(|e| ServerFnError::ServerError(e.to_string()))?;
|
.map_err(|e| ServerFnError::new(e))?;
|
||||||
|
|
||||||
tale.ok_or(ServerFnError::ServerError("Post not found".into()))
|
tale.ok_or(ServerFnError::new(format!("Post {} not found", slug)))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[server]
|
||||||
|
pub async fn get_author(handle: String) -> Result<Adventurer, ServerFnError>
|
||||||
|
{
|
||||||
|
let db = get_database_instance().await;
|
||||||
|
|
||||||
|
let author = db.get_adventurer(&handle).await
|
||||||
|
.map_err(|e| ServerFnError::new(e))?;
|
||||||
|
|
||||||
|
author.ok_or(ServerFnError::new(format!("Author {} not found.", handle)))
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user