diff --git a/src/application/routes/app/timeline.rs b/src/application/routes/app/timeline.rs index 9dbcf88..6aa0f4d 100644 --- a/src/application/routes/app/timeline.rs +++ b/src/application/routes/app/timeline.rs @@ -2,13 +2,12 @@ use axum::extract::{Query, State}; use axum::http::HeaderMap; use axum::http::StatusCode; use axum::response::{IntoResponse, Response}; -use serde::Deserialize; use crate::application::errors::{AppError, map_app_error}; use crate::application::routes::render_html; -use crate::application::routes::support::{is_datastar_request, normalize_request}; +use crate::application::routes::support::{ListQuery, is_datastar_request, normalize_request}; use crate::application::state::AppState; -use crate::domain::listing::{ListRequest, PageSize, SortDirection, SortKey}; +use crate::domain::listing::ListRequest; use crate::domain::timeline::{TimelineEvent, TimelineSortKey}; use crate::presentation::web::templates::{TimelineChunkTemplate, TimelineTemplate}; use crate::presentation::web::views::{ @@ -19,65 +18,15 @@ const TIMELINE_PAGE_PATH: &str = "/timeline"; const TIMELINE_FRAGMENT_PATH: &str = "/timeline"; const TIMELINE_DEFAULT_PAGE_SIZE: u32 = 20; -#[derive(Debug, Deserialize)] -#[serde(untagged)] -enum PageSizeParam { - Number(u32), - Text(String), -} - -#[derive(Debug, Deserialize)] -pub struct TimelineQuery { - page: Option, - #[serde(default)] - page_size: Option, - #[serde(default, rename = "sort")] - sort_key: Option, - #[serde(default, rename = "dir")] - sort_dir: Option, -} - -impl TimelineQuery { - fn to_request(&self) -> ListRequest { - let page = self.page.unwrap_or(1); - let page_size = match &self.page_size { - Some(PageSizeParam::Number(value)) => PageSize::limited(*value), - Some(PageSizeParam::Text(text)) if text.eq_ignore_ascii_case("all") => PageSize::All, - Some(PageSizeParam::Text(text)) => text - .parse::() - .map(PageSize::limited) - .unwrap_or(PageSize::limited(TIMELINE_DEFAULT_PAGE_SIZE)), - None => PageSize::limited(TIMELINE_DEFAULT_PAGE_SIZE), - }; - - let sort_key = self - .sort_key - .as_deref() - .and_then(TimelineSortKey::from_query) - .unwrap_or_else(TimelineSortKey::default); - - let sort_direction = self - .sort_dir - .as_deref() - .and_then(|dir| match dir.to_ascii_lowercase().as_str() { - "asc" => Some(SortDirection::Asc), - "desc" => Some(SortDirection::Desc), - _ => None, - }) - .unwrap_or_else(|| sort_key.default_direction()); - - ListRequest::new(page, page_size, sort_key, sort_direction) - } -} - #[tracing::instrument(skip(state, cookies, headers, query))] pub(crate) async fn timeline_page( State(state): State, cookies: tower_cookies::Cookies, headers: HeaderMap, - Query(query): Query, + Query(query): Query, ) -> Result { - let request = query.to_request(); + let (request, _search) = + query.into_request_and_search_with_default::(TIMELINE_DEFAULT_PAGE_SIZE); let is_authenticated = crate::application::routes::is_authenticated(&state, &cookies).await; if is_datastar_request(&headers) {