115 lines
4.2 KiB
Rust
115 lines
4.2 KiB
Rust
use axum::{
|
|
extract::{Query, Request, State},
|
|
response::{IntoResponse, Redirect, Response},
|
|
};
|
|
use maud::{PreEscaped, html};
|
|
use serde::Deserialize;
|
|
|
|
use crate::{
|
|
MnemoState,
|
|
error::CompositeError,
|
|
quotes::Quote,
|
|
users::{User, auth::UserAuthenticate},
|
|
web::{
|
|
components::{nav::nav, quote::quote},
|
|
icons,
|
|
pages::base,
|
|
},
|
|
};
|
|
|
|
pub mod add;
|
|
|
|
#[derive(Deserialize)]
|
|
pub struct PageQuery {
|
|
page: Option<i64>,
|
|
s: Option<String>,
|
|
}
|
|
|
|
pub async fn page(
|
|
State(state): State<MnemoState>,
|
|
Query(query): Query<PageQuery>,
|
|
req: Request,
|
|
) -> Result<Response, CompositeError> {
|
|
let mut conn = state.pool.acquire().await?;
|
|
let u = match User::authenticate(&mut *conn, req.headers()).await? {
|
|
Some(u) => u,
|
|
None => return Ok(Redirect::to(&format!("/login?r={}", req.uri().path())).into_response()),
|
|
};
|
|
|
|
let page = query.page.unwrap_or(1).max(1);
|
|
let per_page = 10;
|
|
let offset = (page - 1) * per_page;
|
|
|
|
let search = query.s.as_deref().unwrap_or("");
|
|
let quotes = match search {
|
|
"" => Quote::get_chronological_offset(&mut *conn, offset, per_page).await?,
|
|
_ => Quote::get_by_search_query(&mut *conn, search, offset, per_page).await?,
|
|
};
|
|
let total_quotes = Quote::total_count(&mut *conn).await?;
|
|
let total_pages = (total_quotes as f64 / per_page as f64).ceil() as i64;
|
|
|
|
let s_qs = if search.is_empty() {
|
|
String::new()
|
|
} else {
|
|
format!("&s={}", search)
|
|
};
|
|
|
|
Ok(base(
|
|
"Quotes | Mnemosyne",
|
|
html!(
|
|
(nav(Some(&u), req.uri().path()))
|
|
|
|
div class="max-w-4xl mx-auto px-2" {
|
|
div class="my-4 flex justify-between" {
|
|
p class="flex items-center gap-2" {
|
|
span class="text-neutral-500" {(PreEscaped(icons::SCROLL_TEXT))}
|
|
span class="text-2xl font-semibold font-lora" {"Quotes"}
|
|
}
|
|
a href="/quotes/add" class="group border rounded flex items-center gap-1 px-2 border-neutral-200/25 hover:border-neutral-200/45 bg-neutral-400/5 hover:bg-neutral-400/10" {
|
|
span class="text-neutral-300 group-hover:text-neutral-200" {(PreEscaped(icons::PLUS))}
|
|
span class="text-neutral-300 group-hover:text-neutral-200" {"Add quote"}
|
|
}
|
|
}
|
|
form method="get" action="/quotes" {
|
|
input type="text" name="s" class="border w-full border-neutral-200/25 hover:border-neutral-200/45 bg-neutral-950/50 p-2 rounded"
|
|
placeholder="Search quotes..." value={(search)};
|
|
}
|
|
div class="my-2 w-full" {
|
|
p class="ml-auto w-fit text-neutral-500 text-sm flex items-center" {
|
|
span class="scale-[.6]" {(PreEscaped(icons::CALENDAR_ARROW_DOWN))}
|
|
"Chronological"
|
|
}
|
|
}
|
|
div class="flex flex-col gap-4 mb-8" {
|
|
@for q in "es {
|
|
(quote(q))
|
|
}
|
|
|
|
div class="flex justify-between items-center mt-4 text-neutral-400" {
|
|
@if page > 1 {
|
|
a href=(format!("/quotes?page={}{}", (page - 1).max(1), s_qs)) class="px-4 py-2 border border-neutral-200/25 hover:border-neutral-200/45 bg-neutral-200/5 hover:bg-neutral-200/15 rounded" {
|
|
"Previous"
|
|
}
|
|
} @else {
|
|
div {}
|
|
}
|
|
|
|
span {
|
|
"Page " (page) " of " (total_pages.max(1))
|
|
}
|
|
|
|
@if page < total_pages {
|
|
a href=(format!("/quotes?page={}{}", page + 1, s_qs)) class="px-4 py-2 border border-neutral-200/25 hover:border-neutral-200/45 bg-neutral-200/5 hover:bg-neutral-200/15 rounded" {
|
|
"Next"
|
|
}
|
|
} @else {
|
|
div {}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
),
|
|
)
|
|
.into_response())
|
|
}
|