dioxus_docs_kit/components/blog/
blog_meta.rs1use dioxus::prelude::*;
2
3use crate::BlogContext;
4use crate::blog::registry::BlogRegistry;
5
6fn join_site_url(site_url: &str, base_path: &str, slug: Option<&str>) -> String {
7 let mut url = site_url.trim_end_matches('/').to_string();
8
9 if !base_path.is_empty() {
10 if !base_path.starts_with('/') {
11 url.push('/');
12 }
13 url.push_str(base_path.trim_end_matches('/'));
14 }
15
16 if let Some(slug) = slug
17 && !slug.is_empty()
18 {
19 url.push('/');
20 url.push_str(slug.trim_start_matches('/'));
21 }
22
23 url
24}
25
26#[component]
28pub fn BlogPostMeta(slug: String, site_url: String) -> Element {
29 let registry = use_context::<&'static BlogRegistry>();
30 let ctx = use_context::<BlogContext>();
31
32 let post = match registry.get_post(&slug) {
33 Some(p) => p,
34 None => return rsx! {},
35 };
36
37 let title = &post.frontmatter.title;
38 let description = post.frontmatter.description.as_deref().unwrap_or("");
39 let url = join_site_url(&site_url, &ctx.base_path, Some(&slug));
40 let date = &post.frontmatter.date;
41 let author_name = registry
42 .get_author(&post.frontmatter.author)
43 .map(|a| a.name.as_str())
44 .unwrap_or("");
45
46 rsx! {
47 document::Title { "{title}" }
48 document::Meta { name: "description", content: "{description}" }
49
50 document::Meta { property: "og:title", content: "{title}" }
52 document::Meta { property: "og:description", content: "{description}" }
53 document::Meta { property: "og:type", content: "article" }
54 document::Meta { property: "og:url", content: "{url}" }
55 if let Some(ref cover) = post.frontmatter.cover_image {
56 document::Meta { property: "og:image", content: "{cover}" }
57 }
58
59 document::Meta { name: "twitter:card", content: "summary_large_image" }
61 document::Meta { name: "twitter:title", content: "{title}" }
62 document::Meta { name: "twitter:description", content: "{description}" }
63 if let Some(ref cover) = post.frontmatter.cover_image {
64 document::Meta { name: "twitter:image", content: "{cover}" }
65 }
66
67 document::Meta { property: "article:published_time", content: "{date}" }
69 if !author_name.is_empty() {
70 document::Meta { property: "article:author", content: "{author_name}" }
71 }
72 for tag in post.frontmatter.tags.iter() {
73 document::Meta { property: "article:tag", content: "{tag}" }
74 }
75 }
76}
77
78#[component]
80pub fn BlogIndexMeta(title: String, description: String, site_url: String) -> Element {
81 let ctx = use_context::<BlogContext>();
82 let url = join_site_url(&site_url, &ctx.base_path, None);
83
84 rsx! {
85 document::Title { "{title}" }
86 document::Meta { name: "description", content: "{description}" }
87 document::Meta { property: "og:title", content: "{title}" }
88 document::Meta { property: "og:description", content: "{description}" }
89 document::Meta { property: "og:type", content: "website" }
90 document::Meta { property: "og:url", content: "{url}" }
91 document::Meta { name: "twitter:card", content: "summary" }
92 document::Meta { name: "twitter:title", content: "{title}" }
93 document::Meta { name: "twitter:description", content: "{description}" }
94 }
95}
96
97#[cfg(test)]
98mod tests {
99 use super::join_site_url;
100
101 #[test]
102 fn joins_site_url_without_duplicate_slashes() {
103 assert_eq!(
104 join_site_url("https://example.com/", "/blog/", Some("hello-world")),
105 "https://example.com/blog/hello-world"
106 );
107 assert_eq!(
108 join_site_url("https://example.com", "blog", Some("/hello-world")),
109 "https://example.com/blog/hello-world"
110 );
111 assert_eq!(
112 join_site_url("https://example.com/", "/blog/", None),
113 "https://example.com/blog"
114 );
115 }
116}