paginate books
This commit is contained in:
parent
6d949bb21e
commit
6a79f0c1ed
@ -24,7 +24,7 @@ impl Calibre {
|
|||||||
&self,
|
&self,
|
||||||
limit: u64,
|
limit: u64,
|
||||||
cursor: Option<&str>,
|
cursor: Option<&str>,
|
||||||
sort_order: SortOrder,
|
sort_order: &SortOrder,
|
||||||
) -> Result<Vec<Book>, DataStoreError> {
|
) -> Result<Vec<Book>, DataStoreError> {
|
||||||
let conn = self.pool.get()?;
|
let conn = self.pool.get()?;
|
||||||
Book::multiple(&conn, limit, cursor, sort_order)
|
Book::multiple(&conn, limit, cursor, sort_order)
|
||||||
@ -91,6 +91,16 @@ impl Calibre {
|
|||||||
Author::has_more_authors(&conn, author_sort)
|
Author::has_more_authors(&conn, author_sort)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn has_previous_books(&self, book_sort: &str) -> Result<bool, DataStoreError> {
|
||||||
|
let conn = self.pool.get()?;
|
||||||
|
Book::has_previous_books(&conn, book_sort)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn has_more_books(&self, book_sort: &str) -> Result<bool, DataStoreError> {
|
||||||
|
let conn = self.pool.get()?;
|
||||||
|
Book::has_more_books(&conn, book_sort)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn scalar_author(&self, id: u64) -> Result<Author, DataStoreError> {
|
pub fn scalar_author(&self, id: u64) -> Result<Author, DataStoreError> {
|
||||||
let conn = self.pool.get()?;
|
let conn = self.pool.get()?;
|
||||||
Author::scalar_author(&conn, id)
|
Author::scalar_author(&conn, id)
|
||||||
@ -108,7 +118,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn books() {
|
fn books() {
|
||||||
let c = init_calibre();
|
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);
|
assert_eq!(books.len(), 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,9 +28,9 @@ impl Book {
|
|||||||
conn: &Connection,
|
conn: &Connection,
|
||||||
limit: u64,
|
limit: u64,
|
||||||
cursor: Option<&str>,
|
cursor: Option<&str>,
|
||||||
sort_order: SortOrder,
|
sort_order: &SortOrder,
|
||||||
) -> Result<Vec<Self>, DataStoreError> {
|
) -> Result<Vec<Self>, DataStoreError> {
|
||||||
let pagination = Pagination::new("sort", cursor, limit, sort_order);
|
let pagination = Pagination::new("sort", cursor, limit, *sort_order);
|
||||||
pagination.paginate(
|
pagination.paginate(
|
||||||
conn,
|
conn,
|
||||||
"SELECT id, title, sort, path FROM books",
|
"SELECT id, title, sort, path FROM books",
|
||||||
@ -71,4 +71,22 @@ impl Book {
|
|||||||
let params = named_params! { ":id": id };
|
let params = named_params! { ":id": id };
|
||||||
Ok(stmt.query_row(params, Self::from_row)?)
|
Ok(stmt.query_row(params, Self::from_row)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn has_previous_books(conn: &Connection, sort_title: &str) -> Result<bool, DataStoreError> {
|
||||||
|
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<bool, DataStoreError> {
|
||||||
|
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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,7 +33,7 @@ pub async fn handler(
|
|||||||
context.insert("books", &books);
|
context.insert("books", &books);
|
||||||
|
|
||||||
TEMPLATES
|
TEMPLATES
|
||||||
.render("books", &context)
|
.render("book_list", &context)
|
||||||
.map_err(InternalServerError)
|
.map_err(InternalServerError)
|
||||||
.map(Html)
|
.map(Html)
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,44 @@
|
|||||||
use std::sync::Arc;
|
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]
|
#[handler]
|
||||||
pub async fn handler(state: Data<&Arc<AppState>>) -> Result<String, poem::Error> {
|
pub async fn handler_init(state: Data<&Arc<AppState>>) -> Result<Html<String>, poem::Error> {
|
||||||
Ok("books".to_string())
|
books(&state, None, &SortOrder::ASC)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[handler]
|
||||||
|
pub async fn handler(
|
||||||
|
Path((cursor, sort_order)): Path<(String, SortOrder)>,
|
||||||
|
state: Data<&Arc<AppState>>,
|
||||||
|
) -> Result<Html<String>, poem::Error> {
|
||||||
|
books(&state, Some(&cursor), &sort_order)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn books(
|
||||||
|
state: &Arc<AppState>,
|
||||||
|
cursor: Option<&str>,
|
||||||
|
sort_order: &SortOrder,
|
||||||
|
) -> Result<Html<String>, 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),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
use std::fmt::Debug;
|
||||||
|
|
||||||
use calibre_db::data::error::DataStoreError;
|
use calibre_db::data::error::DataStoreError;
|
||||||
use poem::{error::InternalServerError, web::Html};
|
use poem::{error::InternalServerError, web::Html};
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
@ -7,7 +9,7 @@ use crate::templates::TEMPLATES;
|
|||||||
|
|
||||||
use super::error::SqliteError;
|
use super::error::SqliteError;
|
||||||
|
|
||||||
pub fn render<T: Serialize, F, S, P, M>(
|
pub fn render<T: Serialize + Debug, F, S, P, M>(
|
||||||
template: &str,
|
template: &str,
|
||||||
fetcher: F,
|
fetcher: F,
|
||||||
sort_field: S,
|
sort_field: S,
|
||||||
@ -39,6 +41,7 @@ where
|
|||||||
context.insert("forward_cursor", &forward_cursor);
|
context.insert("forward_cursor", &forward_cursor);
|
||||||
context.insert("nav", template);
|
context.insert("nav", template);
|
||||||
context.insert(template, &items);
|
context.insert(template, &items);
|
||||||
|
|
||||||
TEMPLATES
|
TEMPLATES
|
||||||
.render(template, &context)
|
.render(template, &context)
|
||||||
.map_err(InternalServerError)
|
.map_err(InternalServerError)
|
||||||
|
@ -24,7 +24,7 @@ pub async fn handler(state: Data<&Arc<AppState>>) -> Result<Html<String>, poem::
|
|||||||
context.insert("nav", "recent");
|
context.insert("nav", "recent");
|
||||||
context.insert("books", &recent_books);
|
context.insert("books", &recent_books);
|
||||||
TEMPLATES
|
TEMPLATES
|
||||||
.render("books", &context)
|
.render("book_list", &context)
|
||||||
.map_err(InternalServerError)
|
.map_err(InternalServerError)
|
||||||
.map(Html)
|
.map(Html)
|
||||||
}
|
}
|
||||||
|
@ -49,7 +49,8 @@ async fn main() -> Result<(), std::io::Error> {
|
|||||||
|
|
||||||
let app = Route::new()
|
let app = Route::new()
|
||||||
.at("/", get(handlers::recents::handler))
|
.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", get(handlers::authors::handler_init))
|
||||||
.at("/authors/:id", get(handlers::author::handler))
|
.at("/authors/:id", get(handlers::author::handler))
|
||||||
.at(
|
.at(
|
||||||
|
@ -7,6 +7,7 @@ pub static TEMPLATES: Lazy<Tera> = Lazy::new(|| {
|
|||||||
("base", include_str!("../templates/base.html")),
|
("base", include_str!("../templates/base.html")),
|
||||||
("book_card", include_str!("../templates/book_card.html")),
|
("book_card", include_str!("../templates/book_card.html")),
|
||||||
("authors", include_str!("../templates/authors.html")),
|
("authors", include_str!("../templates/authors.html")),
|
||||||
|
("book_list", include_str!("../templates/book_list.html")),
|
||||||
("books", include_str!("../templates/books.html")),
|
("books", include_str!("../templates/books.html")),
|
||||||
])
|
])
|
||||||
.expect("failed to parse tera templates");
|
.expect("failed to parse tera templates");
|
||||||
|
8
rusty-library/templates/book_list.html
Normal file
8
rusty-library/templates/book_list.html
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{% extends "base" %}
|
||||||
|
{% block content %}
|
||||||
|
<div class="grid-container">
|
||||||
|
{% for book in books %}
|
||||||
|
{% include "book_card" %}
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
{% endblock content %}
|
@ -1,8 +1,19 @@
|
|||||||
{% extends "base" %}
|
{% extends "base" %}
|
||||||
|
{% block title %}
|
||||||
|
{% if has_previous %}
|
||||||
|
<a class="secondary" href="/books/{{ backward_cursor }}/DESC">← back</a>
|
||||||
|
{% endif %}
|
||||||
|
{% if has_previous and has_more %}|{% endif%}
|
||||||
|
|
||||||
|
{% if has_more %}
|
||||||
|
<a class="secondary" href="/books/{{ forward_cursor }}/ASC">more →</a>
|
||||||
|
{% endif %}
|
||||||
|
{% endblock title %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="grid-container">
|
<div class="grid-container">
|
||||||
{% for book in books %}
|
{% for book in books %}
|
||||||
{% include "book_card" %}
|
{% include "book_card" %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
{% endblock content %}
|
{% endblock content %}
|
||||||
|
Loading…
Reference in New Issue
Block a user