From 6a79f0c1ed4904deb4043999ecf7acad44a92493 Mon Sep 17 00:00:00 2001 From: Sebastian Hugentobler Date: Mon, 6 May 2024 14:42:14 +0200 Subject: [PATCH] paginate books --- calibre-db/src/calibre.rs | 14 +++++++-- calibre-db/src/data/book.rs | 22 +++++++++++-- rusty-library/src/handlers/author.rs | 2 +- rusty-library/src/handlers/books.rs | 42 ++++++++++++++++++++++--- rusty-library/src/handlers/paginated.rs | 5 ++- rusty-library/src/handlers/recents.rs | 2 +- rusty-library/src/main.rs | 3 +- rusty-library/src/templates.rs | 1 + rusty-library/templates/book_list.html | 8 +++++ rusty-library/templates/books.html | 13 +++++++- 10 files changed, 99 insertions(+), 13 deletions(-) create mode 100644 rusty-library/templates/book_list.html diff --git a/calibre-db/src/calibre.rs b/calibre-db/src/calibre.rs index 1eb5d2e..8798a9f 100644 --- a/calibre-db/src/calibre.rs +++ b/calibre-db/src/calibre.rs @@ -24,7 +24,7 @@ impl Calibre { &self, limit: u64, cursor: Option<&str>, - sort_order: SortOrder, + sort_order: &SortOrder, ) -> Result, DataStoreError> { let conn = self.pool.get()?; Book::multiple(&conn, limit, cursor, sort_order) @@ -91,6 +91,16 @@ impl Calibre { Author::has_more_authors(&conn, author_sort) } + pub fn has_previous_books(&self, book_sort: &str) -> Result { + let conn = self.pool.get()?; + Book::has_previous_books(&conn, book_sort) + } + + pub fn has_more_books(&self, book_sort: &str) -> Result { + let conn = self.pool.get()?; + Book::has_more_books(&conn, book_sort) + } + pub fn scalar_author(&self, id: u64) -> Result { let conn = self.pool.get()?; Author::scalar_author(&conn, id) @@ -108,7 +118,7 @@ mod tests { #[test] fn books() { let c = init_calibre(); - let books = c.books(10, None, SortOrder::ASC).unwrap(); + let books = c.books(10, None, &SortOrder::ASC).unwrap(); assert_eq!(books.len(), 4); } diff --git a/calibre-db/src/data/book.rs b/calibre-db/src/data/book.rs index ebf6b0a..d1ffe78 100644 --- a/calibre-db/src/data/book.rs +++ b/calibre-db/src/data/book.rs @@ -28,9 +28,9 @@ impl Book { conn: &Connection, limit: u64, cursor: Option<&str>, - sort_order: SortOrder, + sort_order: &SortOrder, ) -> Result, DataStoreError> { - let pagination = Pagination::new("sort", cursor, limit, sort_order); + let pagination = Pagination::new("sort", cursor, limit, *sort_order); pagination.paginate( conn, "SELECT id, title, sort, path FROM books", @@ -71,4 +71,22 @@ impl Book { let params = named_params! { ":id": id }; Ok(stmt.query_row(params, Self::from_row)?) } + + pub fn has_previous_books(conn: &Connection, sort_title: &str) -> Result { + let mut stmt = conn + .prepare("SELECT Count(1) FROM books WHERE sort < (:sort_title) ORDER BY sort DESC")?; + let params = named_params! { ":sort_title": sort_title}; + let count: u64 = stmt.query_row(params, |x| x.get(0))?; + + Ok(count > 0) + } + + pub fn has_more_books(conn: &Connection, sort_title: &str) -> Result { + let mut stmt = conn + .prepare("SELECT Count(1) FROM books WHERE sort > (:sort_title) ORDER BY sort ASC")?; + let params = named_params! { ":sort_title": sort_title}; + let count: u64 = stmt.query_row(params, |x| x.get(0))?; + + Ok(count > 0) + } } diff --git a/rusty-library/src/handlers/author.rs b/rusty-library/src/handlers/author.rs index 225b9fb..4363462 100644 --- a/rusty-library/src/handlers/author.rs +++ b/rusty-library/src/handlers/author.rs @@ -33,7 +33,7 @@ pub async fn handler( context.insert("books", &books); TEMPLATES - .render("books", &context) + .render("book_list", &context) .map_err(InternalServerError) .map(Html) } diff --git a/rusty-library/src/handlers/books.rs b/rusty-library/src/handlers/books.rs index bf18617..8043715 100644 --- a/rusty-library/src/handlers/books.rs +++ b/rusty-library/src/handlers/books.rs @@ -1,10 +1,44 @@ use std::sync::Arc; -use poem::{handler, web::Data}; +use calibre_db::data::pagination::SortOrder; +use poem::{ + handler, + web::{Data, Html, Path}, +}; -use crate::app_state::AppState; +use crate::{app_state::AppState, data::book::Book}; + +use super::paginated; #[handler] -pub async fn handler(state: Data<&Arc>) -> Result { - Ok("books".to_string()) +pub async fn handler_init(state: Data<&Arc>) -> Result, poem::Error> { + books(&state, None, &SortOrder::ASC) +} + +#[handler] +pub async fn handler( + Path((cursor, sort_order)): Path<(String, SortOrder)>, + state: Data<&Arc>, +) -> Result, poem::Error> { + books(&state, Some(&cursor), &sort_order) +} + +fn books( + state: &Arc, + cursor: Option<&str>, + sort_order: &SortOrder, +) -> Result, poem::Error> { + paginated::render( + "books", + || { + state.calibre.books(25, cursor, sort_order).map(|x| { + x.iter() + .filter_map(|y| Book::full_book(y, &state)) + .collect() + }) + }, + |book| book.sort.clone(), + |cursor| state.calibre.has_previous_books(cursor), + |cursor| state.calibre.has_more_books(cursor), + ) } diff --git a/rusty-library/src/handlers/paginated.rs b/rusty-library/src/handlers/paginated.rs index 9bbfbce..5455e5e 100644 --- a/rusty-library/src/handlers/paginated.rs +++ b/rusty-library/src/handlers/paginated.rs @@ -1,3 +1,5 @@ +use std::fmt::Debug; + use calibre_db::data::error::DataStoreError; use poem::{error::InternalServerError, web::Html}; use serde::Serialize; @@ -7,7 +9,7 @@ use crate::templates::TEMPLATES; use super::error::SqliteError; -pub fn render( +pub fn render( template: &str, fetcher: F, sort_field: S, @@ -39,6 +41,7 @@ where context.insert("forward_cursor", &forward_cursor); context.insert("nav", template); context.insert(template, &items); + TEMPLATES .render(template, &context) .map_err(InternalServerError) diff --git a/rusty-library/src/handlers/recents.rs b/rusty-library/src/handlers/recents.rs index 90047ef..b345a66 100644 --- a/rusty-library/src/handlers/recents.rs +++ b/rusty-library/src/handlers/recents.rs @@ -24,7 +24,7 @@ pub async fn handler(state: Data<&Arc>) -> Result, poem:: context.insert("nav", "recent"); context.insert("books", &recent_books); TEMPLATES - .render("books", &context) + .render("book_list", &context) .map_err(InternalServerError) .map(Html) } diff --git a/rusty-library/src/main.rs b/rusty-library/src/main.rs index b453e76..c34c8b8 100644 --- a/rusty-library/src/main.rs +++ b/rusty-library/src/main.rs @@ -49,7 +49,8 @@ async fn main() -> Result<(), std::io::Error> { let app = Route::new() .at("/", get(handlers::recents::handler)) - .at("/books", get(handlers::books::handler)) + .at("/books", get(handlers::books::handler_init)) + .at("/books/:cursor/:sort_order", get(handlers::books::handler)) .at("/authors", get(handlers::authors::handler_init)) .at("/authors/:id", get(handlers::author::handler)) .at( diff --git a/rusty-library/src/templates.rs b/rusty-library/src/templates.rs index 800ab30..2f9b879 100644 --- a/rusty-library/src/templates.rs +++ b/rusty-library/src/templates.rs @@ -7,6 +7,7 @@ pub static TEMPLATES: Lazy = Lazy::new(|| { ("base", include_str!("../templates/base.html")), ("book_card", include_str!("../templates/book_card.html")), ("authors", include_str!("../templates/authors.html")), + ("book_list", include_str!("../templates/book_list.html")), ("books", include_str!("../templates/books.html")), ]) .expect("failed to parse tera templates"); diff --git a/rusty-library/templates/book_list.html b/rusty-library/templates/book_list.html new file mode 100644 index 0000000..b69059f --- /dev/null +++ b/rusty-library/templates/book_list.html @@ -0,0 +1,8 @@ +{% extends "base" %} +{% block content %} +
+ {% for book in books %} + {% include "book_card" %} + {% endfor %} +
+{% endblock content %} diff --git a/rusty-library/templates/books.html b/rusty-library/templates/books.html index b69059f..1297425 100644 --- a/rusty-library/templates/books.html +++ b/rusty-library/templates/books.html @@ -1,8 +1,19 @@ {% extends "base" %} +{% block title %} +{% if has_previous %} +← back +{% endif %} +{% if has_previous and has_more %}|{% endif%} + +{% if has_more %} +more → +{% endif %} +{% endblock title %} + {% block content %}
{% for book in books %} - {% include "book_card" %} + {% include "book_card" %} {% endfor %}
{% endblock content %}