Implement pagination for newsreader

This commit is contained in:
Bill Thiede 2024-07-22 09:28:12 -07:00
parent 01cbe6c037
commit b5468bced2
2 changed files with 79 additions and 31 deletions

View File

@ -15,4 +15,6 @@ WHERE
) )
ORDER BY ORDER BY
date DESC, date DESC,
title title OFFSET $3
LIMIT
$4

View File

@ -49,13 +49,55 @@ pub async fn search(
before, before,
first, first,
last, last,
|after, before, first, last| async move { |after: Option<usize>, before: Option<usize>, first, last| async move {
info!("search page info {after:#?}, {before:#?}, {first:#?}, {last:#?}"); info!("search page info {after:#?}, {before:#?}, {first:#?}, {last:#?}");
let rows = sqlx::query_file!("sql/threads.sql", site, query.unread_only,) let default_page_size = 100;
let (offset, limit) = match (after, before, first, last) {
// Reasonable defaults
(None, None, None, None) => (0, default_page_size),
(None, None, Some(first), None) => (0, first),
(Some(after), None, None, None) => (after, default_page_size),
(Some(after), None, Some(first), None) => (after, first),
(None, Some(before), None, None) => {
(before.saturating_sub(default_page_size), default_page_size)
}
(None, Some(before), None, Some(last)) => (before.saturating_sub(last), last),
(None, None, None, Some(_)) => {
panic!("specifying last and no before doesn't make sense")
}
(None, None, Some(_), Some(_)) => {
panic!("specifying first and last doesn't make sense")
}
(None, Some(_), Some(_), _) => {
panic!("specifying before and first doesn't make sense")
}
(Some(_), Some(_), _, _) => {
panic!("specifying after and before doesn't make sense")
}
(Some(_), None, None, Some(_)) => {
panic!("specifying after and last doesn't make sense")
}
(Some(_), None, Some(_), Some(_)) => {
panic!("specifying after, first and last doesn't make sense")
}
};
// The +1 is to see if there are more pages of data available.
let limit = limit + 1;
info!("search page offset {offset} limit {limit}");
let rows = sqlx::query_file!(
"sql/threads.sql",
site,
query.unread_only,
offset as i64,
limit as i64
)
.fetch_all(pool) .fetch_all(pool)
.await?; .await?;
let slice = rows.into_iter().map(|r| { let mut slice = rows
.into_iter()
.map(|r| {
let tags = if r.is_read.unwrap_or(false) { let tags = if r.is_read.unwrap_or(false) {
vec![site.clone()] vec![site.clone()]
} else { } else {
@ -75,15 +117,18 @@ pub async fn search(
subject: r.title.unwrap_or("NO TITLE".to_string()), subject: r.title.unwrap_or("NO TITLE".to_string()),
tags, tags,
} }
}); })
let mut connection = Connection::new(false, false); .collect::<Vec<_>>();
// TODO let has_more = slice.len() == limit;
let start = 0; let mut connection = Connection::new(offset > 0, has_more);
if has_more {
slice.pop();
};
connection.edges.extend( connection.edges.extend(
slice slice
.into_iter() .into_iter()
.enumerate() .enumerate()
.map(|(idx, item)| Edge::new(start + idx, item)), .map(|(idx, item)| Edge::new(offset + idx, item)),
); );
Ok::<_, async_graphql::Error>(connection) Ok::<_, async_graphql::Error>(connection)
}, },
@ -91,7 +136,8 @@ pub async fn search(
.await .await
} }
pub async fn tags(pool: &PgPool, needs_unread: bool) -> Result<Vec<Tag>, ServerError> { pub async fn tags(pool: &PgPool, _needs_unread: bool) -> Result<Vec<Tag>, ServerError> {
// TODO: optimize query by using needs_unread
let tags = sqlx::query_file!("sql/tags.sql").fetch_all(pool).await?; let tags = sqlx::query_file!("sql/tags.sql").fetch_all(pool).await?;
let tags = tags let tags = tags
.into_iter() .into_iter()