Skip to main content

max / makenotwork

2.4 KB · 80 lines History Blame Raw
1 //! Feed page: items from followed users, projects, and tags.
2
3 use axum::extract::{Query, State};
4 use axum::response::IntoResponse;
5 use serde::Deserialize;
6 use tower_sessions::Session;
7
8 use crate::{
9 auth::AuthUser,
10 constants,
11 db,
12 error::Result,
13 helpers::get_csrf_token,
14 templates::FeedTemplate,
15 types::DiscoverItem,
16 AppState,
17 };
18
19 /// Query parameters for the feed page.
20 #[derive(Debug, Deserialize)]
21 pub struct FeedQuery {
22 pub page: Option<u32>,
23 }
24
25 /// Build a sliding window of page numbers for pagination controls.
26 pub(super) fn build_pagination_range(current_page: u32, total_pages: u32) -> Vec<u32> {
27 if total_pages <= constants::PAGINATION_WINDOW_SIZE {
28 (1..=total_pages).collect()
29 } else {
30 let start = current_page.saturating_sub(2).max(1);
31 let end = (start + 4).min(total_pages);
32 let start = end.saturating_sub(4).max(1);
33 (start..=end).collect()
34 }
35 }
36
37 /// GET /feed: paginated feed of items from followed users, projects, and tags.
38 #[tracing::instrument(skip_all, name = "feed::feed_page")]
39 pub(super) async fn feed_page(
40 State(state): State<AppState>,
41 session: Session,
42 AuthUser(user): AuthUser,
43 Query(query): Query<FeedQuery>,
44 ) -> Result<impl IntoResponse> {
45 let csrf_token = get_csrf_token(&session).await;
46 let session_user = Some(user.clone());
47
48 let page = query.page.unwrap_or(1).max(1);
49 let offset = ((page - 1) * constants::FEED_PAGE_SIZE) as i64;
50
51 let total_items = db::follows::count_followed_feed_items(&state.db, user.id).await? as u32;
52 let total_pages = (total_items + constants::FEED_PAGE_SIZE - 1) / constants::FEED_PAGE_SIZE.max(1);
53
54 let db_items = db::follows::get_followed_feed_items(
55 &state.db,
56 user.id,
57 constants::FEED_PAGE_SIZE as i64,
58 offset,
59 )
60 .await?;
61
62 let items: Vec<DiscoverItem> = db_items.into_iter().map(DiscoverItem::from).collect();
63
64 let showing_start = if total_items == 0 { 0 } else { offset as u32 + 1 };
65 let showing_end = (offset as u32 + constants::FEED_PAGE_SIZE).min(total_items);
66 let pagination_range = build_pagination_range(page, total_pages);
67
68 Ok(FeedTemplate {
69 csrf_token,
70 session_user,
71 items,
72 total_items,
73 current_page: page,
74 total_pages,
75 pagination_range,
76 showing_start,
77 showing_end,
78 })
79 }
80