1use dioxus::prelude::*;
13
14use crate::api::posts::{list_published_posts, PostListResponse};
15use crate::components::empty_state::EmptyState;
16use crate::components::post_card::PostCard;
17use crate::components::skeletons::delayed_skeleton::DelayedSkeleton;
18use crate::components::skeletons::home_skeleton::HomeSkeleton;
19use crate::components::ui::Pagination;
20use crate::router::Route;
21
22const POSTS_PER_PAGE: i32 = 10;
24
25#[component]
29pub fn Home() -> Element {
30 rsx! {
31 HomePage { page: 1 }
32 }
33}
34
35#[component]
39pub fn HomePage(page: i32) -> Element {
40 let current_page = page.max(1);
41
42 rsx! {
43 div { class: "animate-page-enter",
44 HomeInfo {}
45 HomePosts { current_page }
46 }
47 }
48}
49
50#[component]
63fn HomePosts(current_page: i32) -> Element {
64 let router = dioxus::router::router();
65
66 let posts_res = use_server_future(move || {
67 let page = match router.current::<Route>() {
68 Route::HomePage { page } => page.max(1),
69 _ => current_page,
71 };
72 list_published_posts(page, POSTS_PER_PAGE)
73 })?;
74
75 let posts_data = posts_res.read().as_ref().map(|r| match r {
77 Ok(PostListResponse { posts, total }) => Ok((posts.clone(), *total)),
78 Err(e) => Err(e.to_string()),
79 });
80
81 match posts_data {
82 Some(Ok((posts, total))) => {
83 rsx! {
84 for post in posts.iter() {
85 PostCard { key: "{post.id}", post: post.clone() }
86 }
87 if total == 0 {
91 EmptyState {
92 title: "还没有文章",
93 description: "这里会收集你写下的每一篇文字。",
94 }
95 }
96 if total > 0 {
99 Pagination {
100 variant: "frontend",
101 current_page,
102 total,
103 per_page: POSTS_PER_PAGE,
104 prev_route: if current_page - 1 <= 1 { Route::Home {} } else { Route::HomePage {
105 page: current_page - 1,
106 } },
107 next_route: Route::HomePage {
108 page: current_page + 1,
109 },
110 unit: "篇",
111 }
112 }
113 }
114 }
115 Some(Err(_)) => {
117 rsx! {
118 div { class: "text-center text-red-500 dark:text-red-400 py-20", "加载失败" }
119 }
120 }
121 _ => {
122 rsx! {
123 DelayedSkeleton { HomeSkeleton {} }
124 }
125 }
126 }
127}
128
129#[component]
131fn HomeInfo() -> Element {
132 rsx! {
133 div { class: "mt-12 mb-20 flex flex-col items-start gap-4",
134 h1 { class: "text-6xl md:text-[5rem] font-extrabold text-[var(--color-paper-primary)] tracking-tighter leading-none",
135 "Yggdrasil."
136 }
137 p { class: "text-lg md:text-xl text-[var(--color-paper-secondary)] font-medium max-w-lg leading-relaxed",
138 "极简、快速、现代。专注于文字本身的开源博客平台。"
139 }
140 }
141 }
142}