From f2eab97c15121679223fb168998577f828193b0e Mon Sep 17 00:00:00 2001 From: jmanczak Date: Wed, 8 Apr 2026 03:01:52 +0200 Subject: [PATCH] quote chronological cursor scroll, icon --- src/quotes/mod.rs | 18 ++++++++++++ src/web/icons/calendar-arrow-down.svg | 1 + src/web/icons/mod.rs | 1 + src/web/pages/quotes.rs | 41 ++++++++++++++++++++------- 4 files changed, 50 insertions(+), 11 deletions(-) create mode 100644 src/web/icons/calendar-arrow-down.svg diff --git a/src/quotes/mod.rs b/src/quotes/mod.rs index 865219b..9272f90 100644 --- a/src/quotes/mod.rs +++ b/src/quotes/mod.rs @@ -113,6 +113,24 @@ impl Quote { None => Ok(None), } } + pub fn get_chronological_cursorscroll( + conn: &Connection, + cursor: Option, + amount: i64, + ) -> Result, QuoteError> { + let ids = match cursor { + Some(c) => conn + .prepare("SELECT id FROM quotes WHERE id < ?1 ORDER BY id DESC LIMIT ?2")? + .query_map((c, amount), |r| r.get(0))? + .collect::, _>>()?, + None => conn + .prepare("SELECT id FROM quotes ORDER BY id DESC LIMIT ?1")? + .query_map((amount,), |r| r.get(0))? + .collect::, _>>()?, + }; + + ids.iter().map(|id| Self::get_by_id(conn, *id)).collect() + } pub fn create( conn: &Connection, lines: Vec<(String, Name)>, diff --git a/src/web/icons/calendar-arrow-down.svg b/src/web/icons/calendar-arrow-down.svg new file mode 100644 index 0000000..b3cea70 --- /dev/null +++ b/src/web/icons/calendar-arrow-down.svg @@ -0,0 +1 @@ + diff --git a/src/web/icons/mod.rs b/src/web/icons/mod.rs index ba62ff8..b7c47f3 100644 --- a/src/web/icons/mod.rs +++ b/src/web/icons/mod.rs @@ -1,6 +1,7 @@ // Below icons sourced from https://lucide.dev pub const ARROW_RIGHT: &str = include_str!("arrow-right.svg"); pub const CALENDAR_1: &str = include_str!("calendar-1.svg"); +pub const CALENDAR_ARROW_DOWN: &str = include_str!("calendar-arrow-down.svg"); pub const CIRCLE_MINUS: &str = include_str!("circle-minus.svg"); pub const CLIPBOARD_CLOCK: &str = include_str!("clipboard-clock.svg"); pub const CLOCK: &str = include_str!("clock.svg"); diff --git a/src/web/pages/quotes.rs b/src/web/pages/quotes.rs index b4b5da8..46d5c47 100644 --- a/src/web/pages/quotes.rs +++ b/src/web/pages/quotes.rs @@ -5,20 +5,31 @@ use axum::{ use maud::{PreEscaped, html}; use crate::{ + database, error::CompositeError, - users::{User, auth::UserAuthenticate}, - web::{components::nav::nav, icons, pages::base}, + quotes::Quote, + users::{ + User, + auth::{UserAuthRequired, UserAuthenticate}, + }, + web::{ + components::{nav::nav, quote::quote}, + icons, + pages::base, + }, }; pub mod add; pub async fn page(req: Request) -> Result { - let u = User::authenticate(req.headers())?; + let u = User::authenticate(req.headers())?.required()?; + let conn = database::conn()?; + let quotes = Quote::get_chronological_cursorscroll(&conn, None, 20)?; Ok(base( "Quotes | Mnemosyne", html!( - (nav(u.as_ref(), req.uri().path())) + (nav(Some(&u), req.uri().path())) div class="max-w-4xl mx-auto px-2" { div class="my-4 flex justify-between" { @@ -26,16 +37,24 @@ pub async fn page(req: Request) -> Result { span class="text-neutral-500" {(PreEscaped(icons::SCROLL_TEXT))} span class="text-2xl font-semibold font-lora" {"Quotes"} } - @if let Some(_) = u { - 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"} - } + 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"} } } input class="border w-full border-neutral-200/25 hover:border-neutral-200/45 bg-neutral-950/50 p-2 rounded" - placeholder="Search for quotes..."; - div class="text-center p-4" {"Search not yet implemented."} + placeholder="Search not yet implemented."; + 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" { + @for q in quotes { + (quote(&q)) + } + } } ), )