feat: SSR for home and tags pages
This commit is contained in:
parent
6b3e086628
commit
6cfe664085
@ -4,65 +4,66 @@ use crate::api::posts::{list_published_posts, PostListResponse};
|
|||||||
use crate::components::nav::use_nav_items;
|
use crate::components::nav::use_nav_items;
|
||||||
use crate::components::page_layout::PageLayout;
|
use crate::components::page_layout::PageLayout;
|
||||||
use crate::components::post_card::PostCard;
|
use crate::components::post_card::PostCard;
|
||||||
use crate::hooks::delayed_loading::use_delayed_loading;
|
use crate::components::suspense_wrapper::SuspenseWrapper;
|
||||||
use crate::router::Route;
|
use crate::router::Route;
|
||||||
|
|
||||||
const POSTS_PER_PAGE: i32 = 10;
|
const POSTS_PER_PAGE: i32 = 10;
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
pub fn Home() -> Element {
|
pub fn Home() -> Element {
|
||||||
rsx! { HomeContent { page: 1 } }
|
rsx! { HomePage { page: 1 } }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
pub fn HomePage(page: i32) -> Element {
|
pub fn HomePage(page: i32) -> Element {
|
||||||
rsx! { HomeContent { page } }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[component]
|
|
||||||
fn HomeContent(page: i32) -> Element {
|
|
||||||
let route = use_route::<Route>();
|
let route = use_route::<Route>();
|
||||||
let current_page = page.max(1);
|
|
||||||
let posts_res = use_resource(move || list_published_posts(current_page, POSTS_PER_PAGE));
|
|
||||||
let nav_items = use_nav_items(route);
|
let nav_items = use_nav_items(route);
|
||||||
let show_skeleton = use_delayed_loading(move || posts_res.read().is_none());
|
let current_page = page.max(1);
|
||||||
|
|
||||||
rsx! {
|
rsx! {
|
||||||
PageLayout { nav_items,
|
PageLayout { nav_items,
|
||||||
HomeInfo {}
|
HomeInfo {}
|
||||||
match &*posts_res.read() {
|
SuspenseWrapper {
|
||||||
Some(Ok(PostListResponse { posts })) => {
|
HomePosts { current_page }
|
||||||
rsx! {
|
}
|
||||||
for post in posts.iter() {
|
}
|
||||||
PostCard { post: post.clone() }
|
}
|
||||||
}
|
}
|
||||||
if posts.is_empty() {
|
|
||||||
div { class: "text-center text-gray-500 dark:text-[#9b9c9d] py-20",
|
#[component]
|
||||||
"暂无文章"
|
fn HomePosts(current_page: i32) -> Element {
|
||||||
}
|
let posts_res = use_server_future(move || list_published_posts(current_page, POSTS_PER_PAGE))?;
|
||||||
}
|
|
||||||
Pagination { current_page, posts: posts.clone() }
|
let posts_data = posts_res.read().as_ref().map(|r| match r {
|
||||||
|
Ok(PostListResponse { posts }) => Ok(posts.clone()),
|
||||||
|
Err(e) => Err(e.to_string()),
|
||||||
|
});
|
||||||
|
|
||||||
|
match posts_data {
|
||||||
|
Some(Ok(posts)) => {
|
||||||
|
rsx! {
|
||||||
|
for post in posts.iter() {
|
||||||
|
PostCard { post: post.clone() }
|
||||||
|
}
|
||||||
|
if posts.is_empty() {
|
||||||
|
div { class: "text-center text-gray-500 dark:text-[#9b9c9d] py-20",
|
||||||
|
"暂无文章"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some(Err(e)) => {
|
Pagination { current_page, posts: posts.clone() }
|
||||||
rsx! {
|
}
|
||||||
div { class: "text-center text-red-500 dark:text-red-400 py-20",
|
}
|
||||||
"加载失败: {e}"
|
Some(Err(e)) => {
|
||||||
}
|
rsx! {
|
||||||
}
|
div { class: "text-center text-red-500 dark:text-red-400 py-20",
|
||||||
|
"加载失败: {e}"
|
||||||
}
|
}
|
||||||
None => {
|
}
|
||||||
rsx! {
|
}
|
||||||
div { class: if show_skeleton() { "space-y-6 py-4" } else { "space-y-6 py-4 opacity-0" },
|
_ => {
|
||||||
for _ in 0..3 {
|
rsx! {
|
||||||
div { class: "mb-6 p-6 bg-white dark:bg-[#2e2e33] rounded-lg border border-gray-200 dark:border-[#333] animate-pulse",
|
div { class: "text-center text-gray-500 dark:text-[#9b9c9d] py-20",
|
||||||
div { class: "h-7 w-3/4 bg-gray-200 dark:bg-[#2a2a2a] rounded mb-3" }
|
"加载中..."
|
||||||
div { class: "h-4 w-full bg-gray-200 dark:bg-[#2a2a2a] rounded mb-2" }
|
|
||||||
div { class: "h-4 w-2/3 bg-gray-200 dark:bg-[#2a2a2a] rounded" }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,15 +4,13 @@ use crate::api::posts::{get_posts_by_tag, list_tags, PostListResponse, TagListRe
|
|||||||
use crate::components::nav::use_nav_items;
|
use crate::components::nav::use_nav_items;
|
||||||
use crate::components::page_layout::PageLayout;
|
use crate::components::page_layout::PageLayout;
|
||||||
use crate::components::post_card::PostCard;
|
use crate::components::post_card::PostCard;
|
||||||
use crate::hooks::delayed_loading::use_delayed_loading;
|
use crate::components::suspense_wrapper::SuspenseWrapper;
|
||||||
use crate::router::Route;
|
use crate::router::Route;
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
pub fn Tags() -> Element {
|
pub fn Tags() -> Element {
|
||||||
let route = use_route::<Route>();
|
let route = use_route::<Route>();
|
||||||
let tags_res = use_resource(list_tags);
|
|
||||||
let nav_items = use_nav_items(route);
|
let nav_items = use_nav_items(route);
|
||||||
let show_skeleton = use_delayed_loading(move || tags_res.read().is_none());
|
|
||||||
|
|
||||||
rsx! {
|
rsx! {
|
||||||
PageLayout { nav_items,
|
PageLayout { nav_items,
|
||||||
@ -20,72 +18,63 @@ pub fn Tags() -> Element {
|
|||||||
h1 { class: "text-[34px] font-bold text-gray-900 dark:text-[#dadadb]",
|
h1 { class: "text-[34px] font-bold text-gray-900 dark:text-[#dadadb]",
|
||||||
"标签"
|
"标签"
|
||||||
}
|
}
|
||||||
match &*tags_res.read() {
|
}
|
||||||
Some(Ok(TagListResponse { tags })) => {
|
SuspenseWrapper {
|
||||||
let total = tags.iter().map(|t| t.post_count).sum::<i64>();
|
TagsContent {}
|
||||||
rsx! {
|
}
|
||||||
div { class: "mt-2 text-base text-gray-500 dark:text-[#9b9c9d]",
|
}
|
||||||
"共 "
|
}
|
||||||
span { class: "font-medium text-gray-700 dark:text-[#dadadb]", "{tags.len()}" }
|
}
|
||||||
" 个标签,"
|
|
||||||
span { class: "font-medium text-gray-700 dark:text-[#dadadb]", "{total}" }
|
#[component]
|
||||||
" 篇文章"
|
fn TagsContent() -> Element {
|
||||||
}
|
let tags_res = use_server_future(list_tags)?;
|
||||||
}
|
|
||||||
}
|
let tags_data = tags_res.read().as_ref().map(|r| match r {
|
||||||
Some(Err(_)) => {
|
Ok(TagListResponse { tags }) => Ok(tags.clone()),
|
||||||
rsx! {
|
Err(e) => Err(e.to_string()),
|
||||||
div { class: "mt-2 text-base text-gray-500 dark:text-[#9b9c9d]",
|
});
|
||||||
"加载失败"
|
|
||||||
}
|
match tags_data {
|
||||||
}
|
Some(Ok(tags)) => {
|
||||||
}
|
let total = tags.iter().map(|t| t.post_count).sum::<i64>();
|
||||||
None => {
|
rsx! {
|
||||||
rsx! {
|
div { class: "mt-2 text-base text-gray-500 dark:text-[#9b9c9d]",
|
||||||
div { class: "mt-2 text-base text-gray-500 dark:text-[#9b9c9d]",
|
"共 "
|
||||||
"加载中..."
|
span { class: "font-medium text-gray-700 dark:text-[#dadadb]", "{tags.len()}" }
|
||||||
|
" 个标签,"
|
||||||
|
span { class: "font-medium text-gray-700 dark:text-[#dadadb]", "{total}" }
|
||||||
|
" 篇文章"
|
||||||
|
}
|
||||||
|
ul { class: "flex flex-wrap gap-4 mt-6",
|
||||||
|
for tag in tags {
|
||||||
|
li {
|
||||||
|
a {
|
||||||
|
class: "inline-flex items-center px-3 py-1.5 text-base font-medium bg-gray-100 dark:bg-[#2e2e33] text-gray-700 dark:text-[#9b9c9d] rounded-lg hover:bg-gray-200 dark:hover:bg-[#333] transition-colors",
|
||||||
|
href: "/tags/{tag.name}",
|
||||||
|
onclick: move |evt| {
|
||||||
|
evt.prevent_default();
|
||||||
|
dioxus::router::navigator().push(format!("/tags/{}", tag.name).as_str());
|
||||||
|
},
|
||||||
|
"{tag.name}"
|
||||||
|
sup { class: "ml-1 text-sm text-gray-500 dark:text-[#9b9c9d]", "{tag.post_count}" }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
match &*tags_res.read() {
|
}
|
||||||
Some(Ok(TagListResponse { tags })) => {
|
Some(Err(_)) => {
|
||||||
let tags = tags.clone();
|
rsx! {
|
||||||
rsx! {
|
div { class: "text-center text-red-500 dark:text-red-400 py-20",
|
||||||
ul { class: "flex flex-wrap gap-4 mt-6",
|
"加载失败"
|
||||||
for tag in tags.into_iter() {
|
|
||||||
li {
|
|
||||||
a {
|
|
||||||
class: "inline-flex items-center px-3 py-1.5 text-base font-medium bg-gray-100 dark:bg-[#2e2e33] text-gray-700 dark:text-[#9b9c9d] rounded-lg hover:bg-gray-200 dark:hover:bg-[#333] transition-colors",
|
|
||||||
href: "/tags/{tag.name}",
|
|
||||||
onclick: move |evt| {
|
|
||||||
evt.prevent_default();
|
|
||||||
dioxus::router::navigator().push(format!("/tags/{}", tag.name).as_str());
|
|
||||||
},
|
|
||||||
"{tag.name}"
|
|
||||||
sup { class: "ml-1 text-sm text-gray-500 dark:text-[#9b9c9d]", "{tag.post_count}" }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Some(Err(_)) => {
|
}
|
||||||
rsx! {
|
}
|
||||||
div { class: "text-center text-red-500 dark:text-red-400 py-20",
|
_ => {
|
||||||
"加载失败"
|
rsx! {
|
||||||
}
|
div { class: "text-center text-gray-500 dark:text-[#9b9c9d] py-20",
|
||||||
}
|
"加载中..."
|
||||||
}
|
|
||||||
None => {
|
|
||||||
rsx! {
|
|
||||||
div { class: if show_skeleton() { "flex flex-wrap gap-4 mt-6 animate-pulse" } else { "flex flex-wrap gap-4 mt-6 opacity-0" },
|
|
||||||
for _ in 0..8 {
|
|
||||||
div { class: "h-8 w-16 bg-gray-200 dark:bg-[#2a2a2a] rounded-lg" }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -95,10 +84,7 @@ pub fn Tags() -> Element {
|
|||||||
#[component]
|
#[component]
|
||||||
pub fn TagDetail(tag: String) -> Element {
|
pub fn TagDetail(tag: String) -> Element {
|
||||||
let route = use_route::<Route>();
|
let route = use_route::<Route>();
|
||||||
let tag_clone = tag.clone();
|
|
||||||
let posts_res = use_resource(move || get_posts_by_tag(tag_clone.clone()));
|
|
||||||
let nav_items = use_nav_items(route);
|
let nav_items = use_nav_items(route);
|
||||||
let show_skeleton = use_delayed_loading(move || posts_res.read().is_none());
|
|
||||||
|
|
||||||
rsx! {
|
rsx! {
|
||||||
PageLayout { nav_items,
|
PageLayout { nav_items,
|
||||||
@ -106,59 +92,47 @@ pub fn TagDetail(tag: String) -> Element {
|
|||||||
h1 { class: "text-[34px] font-bold text-gray-900 dark:text-[#dadadb]",
|
h1 { class: "text-[34px] font-bold text-gray-900 dark:text-[#dadadb]",
|
||||||
"{tag}"
|
"{tag}"
|
||||||
}
|
}
|
||||||
match &*posts_res.read() {
|
}
|
||||||
Some(Ok(PostListResponse { posts })) => {
|
SuspenseWrapper {
|
||||||
rsx! {
|
TagDetailContent { tag: tag.clone() }
|
||||||
div { class: "mt-2 text-base text-gray-500 dark:text-[#9b9c9d]",
|
}
|
||||||
"共 "
|
}
|
||||||
span { class: "font-medium text-gray-700 dark:text-[#dadadb]", "{posts.len()}" }
|
}
|
||||||
" 篇文章"
|
}
|
||||||
}
|
|
||||||
}
|
#[component]
|
||||||
}
|
fn TagDetailContent(tag: String) -> Element {
|
||||||
Some(Err(_)) => {
|
let posts_res = use_server_future(move || get_posts_by_tag(tag.clone()))?;
|
||||||
rsx! {
|
|
||||||
div { class: "mt-2 text-base text-gray-500 dark:text-[#9b9c9d]",
|
let posts_data = posts_res.read().as_ref().map(|r| match r {
|
||||||
"加载失败"
|
Ok(PostListResponse { posts }) => Ok(posts.clone()),
|
||||||
}
|
Err(e) => Err(e.to_string()),
|
||||||
}
|
});
|
||||||
}
|
|
||||||
None => {
|
match posts_data {
|
||||||
rsx! {
|
Some(Ok(posts)) => {
|
||||||
div { class: "mt-2 text-base text-gray-500 dark:text-[#9b9c9d]",
|
rsx! {
|
||||||
"加载中..."
|
div { class: "mt-2 text-base text-gray-500 dark:text-[#9b9c9d]",
|
||||||
}
|
"共 "
|
||||||
}
|
span { class: "font-medium text-gray-700 dark:text-[#dadadb]", "{posts.len()}" }
|
||||||
}
|
" 篇文章"
|
||||||
|
}
|
||||||
|
for post in posts.iter() {
|
||||||
|
PostCard { post: post.clone() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
match &*posts_res.read() {
|
}
|
||||||
Some(Ok(PostListResponse { posts })) => {
|
Some(Err(_)) => {
|
||||||
rsx! {
|
rsx! {
|
||||||
for post in posts.iter() {
|
div { class: "text-center text-red-500 dark:text-red-400 py-20",
|
||||||
PostCard { post: post.clone() }
|
"加载失败"
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Some(Err(_)) => {
|
}
|
||||||
rsx! {
|
}
|
||||||
div { class: "text-center text-red-500 dark:text-red-400 py-20",
|
_ => {
|
||||||
"加载失败"
|
rsx! {
|
||||||
}
|
div { class: "text-center text-gray-500 dark:text-[#9b9c9d] py-20",
|
||||||
}
|
"加载中..."
|
||||||
}
|
|
||||||
None => {
|
|
||||||
rsx! {
|
|
||||||
div { class: if show_skeleton() { "space-y-6 py-4 animate-pulse" } else { "space-y-6 py-4 opacity-0" },
|
|
||||||
for _ in 0..3 {
|
|
||||||
div { class: "mb-6 p-6 bg-white dark:bg-[#2e2e33] rounded-lg border border-gray-200 dark:border-[#333]",
|
|
||||||
div { class: "h-7 w-3/4 bg-gray-200 dark:bg-[#2a2a2a] rounded mb-3" }
|
|
||||||
div { class: "h-4 w-full bg-gray-200 dark:bg-[#2a2a2a] rounded mb-2" }
|
|
||||||
div { class: "h-4 w-2/3 bg-gray-200 dark:bg-[#2a2a2a] rounded" }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user