I had trouble getting paging to work in a situation where the user should be able to load an initial page and then later move to the next page and the previous pages. If the app hits the prev_page() call when there is no data in the previous page, it effectively is a dead end for the app. Instead, it should just be a noop.
(Commit message body)
Improve the handling of next and previous pages in the Page struct
by fixing a bug that caused the URLs to be set to None when a page with
no data was loaded. This change allows for the next and previous page
URLs to be preserved for future retrieval even when there are no
results in the current page.
I tested this change using a small example app that I've been using to explore the mastodon-async functionality in depth (this could probably be used as a full example perhaps later, but for now it's just for proving out bits and pieces)
#[instrument(name = "home", skip_all, err)]
async fn show_timeline(client: &Mastodon) -> Result<()> {
let mut timeline = load_home_timeline(client).await?;
// log the initial page links
log_page_links(&timeline);
// intentionally load the previous page while we're at the first page to
// check that the behavior of the page object doesn't dead-end at the
// beginning. This should not fail, but it also should not update the page
// links
load_prev_page(&mut timeline).await?;
// this should log the same as the initial page links
log_page_links(&timeline);
// moving to the next page should load the next page and update the page
// links
load_next_page(&mut timeline).await?;
// this should log two different links
log_page_links(&timeline);
// this should move back to the initial page
load_prev_page(&mut timeline).await?;
// this should log the same as the initial page links
log_page_links(&timeline);
Ok(())
}
#[instrument(name = "initial", skip_all, err)]
async fn load_home_timeline(client: &Mastodon) -> Result<Page<Status>> {
let timeline = client
.get_home_timeline()
.await
.context("Couldn't get timeline")?;
info!("loaded initial page of home timeline");
for item in &timeline.initial_items {
debug!(uri = %item.uri);
}
Ok(timeline)
}
#[instrument(name = "next_page", skip_all, err)]
async fn load_next_page(timeline: &mut Page<Status>) -> Result<()> {
let url = timeline.next.clone().context("no next page")?;
let page = timeline
.next_page()
.await
.context("Couldn't get next page")?;
info!(%url, "loaded next page");
log_page_items(page);
Ok(())
}
#[instrument(name = "prev_page", skip_all, err)]
async fn load_prev_page(timeline: &mut Page<Status>) -> Result<()> {
let url = timeline.prev.clone().context("no prev page")?;
let page = timeline
.prev_page()
.await
.context("Couldn't get prev page")?;
info!(%url, "loaded prev page");
log_page_items(page);
Ok(())
}
fn log_page_items(page: Option<Vec<Status>>) {
page.map_or_else(
|| warn!("the page loaded successfully, but there is no data"),
|items| {
for item in items {
debug!(uri = %item.uri);
}
},
);
}
/// This exists because there was an issue with the way that the previous and
/// next pages were loaded when going to the previous page at the beginning or
/// the next page at the end.
fn log_page_links(page: &Page<Status>) {
debug!(
prev = page.prev.as_ref().map_or("None", |u| u.as_str()),
next = page.next.as_ref().map_or("None", |u| u.as_str()),
"page links"
);
}
This loads a home timeline, tries to go to the previous link, then tries to go to the next (which cannot succeed as the links are now both set to none.
Here's the log before the change:
I had trouble getting paging to work in a situation where the user should be able to load an initial page and then later move to the next page and the previous pages. If the app hits the prev_page() call when there is no data in the previous page, it effectively is a dead end for the app. Instead, it should just be a noop.
(Commit message body) Improve the handling of next and previous pages in the
Page
struct by fixing a bug that caused the URLs to be set to None when a page with no data was loaded. This change allows for the next and previous page URLs to be preserved for future retrieval even when there are no results in the current page.I tested this change using a small example app that I've been using to explore the mastodon-async functionality in depth (this could probably be used as a full example perhaps later, but for now it's just for proving out bits and pieces)
https://github.com/joshka/spike-mastodon/blob/fix-paging/src/main.rs#L188-L273
This loads a home timeline, tries to go to the previous link, then tries to go to the next (which cannot succeed as the links are now both set to none. Here's the log before the change:
(Note I made the next/prev urls public for the purposes of this test)
And after this change: