diff --git a/src/pages/home.rs b/src/pages/home.rs index aff2796..702568a 100644 --- a/src/pages/home.rs +++ b/src/pages/home.rs @@ -4,65 +4,66 @@ use crate::api::posts::{list_published_posts, PostListResponse}; use crate::components::nav::use_nav_items; use crate::components::page_layout::PageLayout; use crate::components::post_card::PostCard; -use crate::hooks::delayed_loading::use_delayed_loading; +use crate::components::suspense_wrapper::SuspenseWrapper; use crate::router::Route; const POSTS_PER_PAGE: i32 = 10; #[component] pub fn Home() -> Element { - rsx! { HomeContent { page: 1 } } + rsx! { HomePage { page: 1 } } } #[component] pub fn HomePage(page: i32) -> Element { - rsx! { HomeContent { page } } -} - -#[component] -fn HomeContent(page: i32) -> Element { let route = use_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 show_skeleton = use_delayed_loading(move || posts_res.read().is_none()); + let current_page = page.max(1); rsx! { PageLayout { nav_items, HomeInfo {} - match &*posts_res.read() { - Some(Ok(PostListResponse { 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", - "暂无文章" - } - } - Pagination { current_page, posts: posts.clone() } + SuspenseWrapper { + HomePosts { current_page } + } + } + } +} + +#[component] +fn HomePosts(current_page: i32) -> Element { + let posts_res = use_server_future(move || list_published_posts(current_page, POSTS_PER_PAGE))?; + + 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)) => { - rsx! { - div { class: "text-center text-red-500 dark:text-red-400 py-20", - "加载失败: {e}" - } - } + Pagination { current_page, posts: posts.clone() } + } + } + 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 { - div { class: "mb-6 p-6 bg-white dark:bg-[#2e2e33] rounded-lg border border-gray-200 dark:border-[#333] animate-pulse", - 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" } - } - } - } - } + } + } + _ => { + rsx! { + div { class: "text-center text-gray-500 dark:text-[#9b9c9d] py-20", + "加载中..." } } } diff --git a/src/pages/tags.rs b/src/pages/tags.rs index d4d7754..e312130 100644 --- a/src/pages/tags.rs +++ b/src/pages/tags.rs @@ -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::page_layout::PageLayout; use crate::components::post_card::PostCard; -use crate::hooks::delayed_loading::use_delayed_loading; +use crate::components::suspense_wrapper::SuspenseWrapper; use crate::router::Route; #[component] pub fn Tags() -> Element { let route = use_route::(); - let tags_res = use_resource(list_tags); let nav_items = use_nav_items(route); - let show_skeleton = use_delayed_loading(move || tags_res.read().is_none()); rsx! { PageLayout { nav_items, @@ -20,72 +18,63 @@ pub fn Tags() -> Element { h1 { class: "text-[34px] font-bold text-gray-900 dark:text-[#dadadb]", "标签" } - match &*tags_res.read() { - Some(Ok(TagListResponse { tags })) => { - let total = tags.iter().map(|t| t.post_count).sum::(); - 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}" } - " 篇文章" - } - } - } - Some(Err(_)) => { - rsx! { - div { class: "mt-2 text-base text-gray-500 dark:text-[#9b9c9d]", - "加载失败" - } - } - } - None => { - rsx! { - div { class: "mt-2 text-base text-gray-500 dark:text-[#9b9c9d]", - "加载中..." + } + SuspenseWrapper { + TagsContent {} + } + } + } +} + +#[component] +fn TagsContent() -> Element { + let tags_res = use_server_future(list_tags)?; + + let tags_data = tags_res.read().as_ref().map(|r| match r { + Ok(TagListResponse { tags }) => Ok(tags.clone()), + Err(e) => Err(e.to_string()), + }); + + match tags_data { + Some(Ok(tags)) => { + let total = tags.iter().map(|t| t.post_count).sum::(); + 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}" } + " 篇文章" + } + 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 })) => { - let tags = tags.clone(); - rsx! { - 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", + "加载失败" } - Some(Err(_)) => { - rsx! { - div { class: "text-center text-red-500 dark:text-red-400 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" } - } - } - } + } + } + _ => { + rsx! { + div { class: "text-center text-gray-500 dark:text-[#9b9c9d] py-20", + "加载中..." } } } @@ -95,10 +84,7 @@ pub fn Tags() -> Element { #[component] pub fn TagDetail(tag: String) -> Element { let route = use_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 show_skeleton = use_delayed_loading(move || posts_res.read().is_none()); rsx! { 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]", "{tag}" } - match &*posts_res.read() { - Some(Ok(PostListResponse { posts })) => { - 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()}" } - " 篇文章" - } - } - } - Some(Err(_)) => { - rsx! { - div { class: "mt-2 text-base text-gray-500 dark:text-[#9b9c9d]", - "加载失败" - } - } - } - None => { - rsx! { - div { class: "mt-2 text-base text-gray-500 dark:text-[#9b9c9d]", - "加载中..." - } - } - } + } + SuspenseWrapper { + TagDetailContent { tag: tag.clone() } + } + } + } +} + +#[component] +fn TagDetailContent(tag: String) -> Element { + let posts_res = use_server_future(move || get_posts_by_tag(tag.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! { + 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 })) => { - rsx! { - for post in posts.iter() { - PostCard { post: post.clone() } - } - } + } + Some(Err(_)) => { + rsx! { + div { class: "text-center text-red-500 dark:text-red-400 py-20", + "加载失败" } - Some(Err(_)) => { - rsx! { - div { class: "text-center text-red-500 dark:text-red-400 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" } - } - } - } - } + } + } + _ => { + rsx! { + div { class: "text-center text-gray-500 dark:text-[#9b9c9d] py-20", + "加载中..." } } }