diff --git a/src/index.rs b/src/index.rs index ec997e6447..18f4d4b007 100644 --- a/src/index.rs +++ b/src/index.rs @@ -907,6 +907,31 @@ impl Index { Ok(entries) } + pub(crate) fn runes_paginated( + &self, + page_size: usize, + page_index: usize, + ) -> Result<(Vec<(RuneId, RuneEntry)>, bool)> { + let mut entries = Vec::new(); + + for result in self + .database + .begin_read()? + .open_table(RUNE_ID_TO_RUNE_ENTRY)? + .iter()? + .rev() + .skip(page_index.saturating_mul(page_size)) + .take(page_size.saturating_add(1)) + { + let (id, entry) = result?; + entries.push((RuneId::load(id.value()), RuneEntry::load(entry.value()))); + } + + let more = entries.len() > page_size; + + Ok((entries, more)) + } + pub(crate) fn encode_rune_balance(id: RuneId, balance: u128, buffer: &mut Vec) { varint::encode_to_vec(id.block.into(), buffer); varint::encode_to_vec(id.tx.into(), buffer); diff --git a/src/subcommand/server.rs b/src/subcommand/server.rs index 98dbf77c02..190e878ade 100644 --- a/src/subcommand/server.rs +++ b/src/subcommand/server.rs @@ -255,6 +255,7 @@ impl Server { .route("/rare.txt", get(Self::rare_txt)) .route("/rune/:rune", get(Self::rune)) .route("/runes", get(Self::runes)) + .route("/runes/:page", get(Self::runes_paginated)) .route("/runes/balances", get(Self::runes_balances)) .route("/sat/:sat", get(Self::sat)) .route("/search", get(Self::search_by_query)) @@ -693,17 +694,44 @@ impl Server { async fn runes( Extension(server_config): Extension>, Extension(index): Extension>, + accept_json: AcceptJson, + ) -> ServerResult { + Self::runes_paginated( + Extension(server_config), + Extension(index), + Path(0), + accept_json, + ) + .await + } + + async fn runes_paginated( + Extension(server_config): Extension>, + Extension(index): Extension>, + Path(page_index): Path, AcceptJson(accept_json): AcceptJson, ) -> ServerResult { task::block_in_place(|| { + let (entries, more) = index.runes_paginated(50, page_index)?; + + let prev = page_index.checked_sub(1); + + let next = more.then_some(page_index + 1); + Ok(if accept_json { - Json(api::Runes { - entries: index.runes()?, + Json(RunesHtml { + entries, + more, + prev, + next, }) .into_response() } else { RunesHtml { - entries: index.runes()?, + entries, + more, + prev, + next, } .page(server_config) .into_response() @@ -2634,7 +2662,7 @@ mod tests { server.assert_response_regex( "/runes", StatusCode::OK, - ".*Runes.*

Runes

\n
    \n
.*", + ".*Runes.*

Runes

\n
    \n
\n
\n prev\n next\n
.*", ); let (txid, id) = server.etch( diff --git a/src/templates/runes.rs b/src/templates/runes.rs index 1e616605fc..bfc7e51760 100644 --- a/src/templates/runes.rs +++ b/src/templates/runes.rs @@ -3,6 +3,9 @@ use super::*; #[derive(Boilerplate, Debug, PartialEq, Serialize, Deserialize)] pub struct RunesHtml { pub entries: Vec<(RuneId, RuneEntry)>, + pub more: bool, + pub prev: Option, + pub next: Option, } impl PageContent for RunesHtml { @@ -29,13 +32,62 @@ mod tests { ..default() } )], + more: false, + prev: None, + next: None, } .to_string(), "

Runes

-" +
+ prev + next +
" + ); + } + + #[test] + fn with_prev_and_next() { + assert_eq!( + RunesHtml { + entries: vec![ + ( + RuneId { block: 0, tx: 0 }, + RuneEntry { + spaced_rune: SpacedRune { + rune: Rune(0), + spacers: 0 + }, + ..Default::default() + } + ), + ( + RuneId { block: 0, tx: 1 }, + RuneEntry { + spaced_rune: SpacedRune { + rune: Rune(2), + spacers: 0 + }, + ..Default::default() + } + ) + ], + prev: Some(1), + next: Some(2), + more: true, + } + .to_string(), + "

Runes

+
    +
  • A
  • +
  • C
  • +
+
+ + +
" ); } } diff --git a/templates/runes.html b/templates/runes.html index d2852d94b7..2de6f972ba 100644 --- a/templates/runes.html +++ b/templates/runes.html @@ -4,3 +4,15 @@

Runes

  • {{ entry.spaced_rune }}
  • %% } +
    + %% if let Some(prev) = self.prev { + + %% } else { + prev + %% } + %% if let Some(next) = self.next { + + %% } else { + next + %% } +
    \ No newline at end of file diff --git a/tests/json_api.rs b/tests/json_api.rs index 4b5292b152..4e668af37a 100644 --- a/tests/json_api.rs +++ b/tests/json_api.rs @@ -568,22 +568,22 @@ fn get_runes() { api::Runes { entries: vec![ ( - RuneId { block: 10, tx: 1 }, + RuneId { block: 24, tx: 1 }, RuneEntry { - block: a.id.block, + block: c.id.block, burned: 0, terms: None, divisibility: 0, - etching: a.output.reveal, + etching: c.output.reveal, mints: 0, - number: 0, + number: 2, premine: 1000, spaced_rune: SpacedRune { - rune: Rune(RUNE), + rune: Rune(RUNE + 2), spacers: 0 }, symbol: Some('¢'), - timestamp: 10, + timestamp: 24, turbo: false, } ), @@ -608,26 +608,29 @@ fn get_runes() { } ), ( - RuneId { block: 24, tx: 1 }, + RuneId { block: 10, tx: 1 }, RuneEntry { - block: c.id.block, + block: a.id.block, burned: 0, terms: None, divisibility: 0, - etching: c.output.reveal, + etching: a.output.reveal, mints: 0, - number: 2, + number: 0, premine: 1000, spaced_rune: SpacedRune { - rune: Rune(RUNE + 2), + rune: Rune(RUNE), spacers: 0 }, symbol: Some('¢'), - timestamp: 24, + timestamp: 10, turbo: false, } ) - ] + ], + more: false, + next: None, + prev: None, } ); }