Skip to content

Commit

Permalink
Add /collections Page (#2561)
Browse files Browse the repository at this point in the history
  • Loading branch information
veryordinally authored Nov 18, 2023
1 parent 87c5394 commit b50b536
Show file tree
Hide file tree
Showing 7 changed files with 237 additions and 10 deletions.
28 changes: 28 additions & 0 deletions src/index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -795,6 +795,34 @@ impl Index {
self.client.get_block(&hash).into_option()
}

pub(crate) fn get_collections_paginated(
&self,
page_size: usize,
page_index: usize,
) -> Result<(Vec<InscriptionId>, bool)> {
let mut collections = self
.database
.begin_read()?
.open_multimap_table(INSCRIPTION_ID_TO_CHILDREN)?
.iter()?
.skip(page_index * page_size)
.take(page_size + 1)
.map(|result| {
result
.map(|(key, _value)| InscriptionId::load(*key.value()))
.map_err(|err| err.into())
})
.collect::<Result<Vec<InscriptionId>>>()?;

let more = collections.len() > page_size;

if more {
collections.pop();
}

Ok((collections, more))
}

#[cfg(test)]
pub(crate) fn get_children_by_inscription_id(
&self,
Expand Down
123 changes: 117 additions & 6 deletions src/subcommand/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@ use {
page_config::PageConfig,
runes::Rune,
templates::{
BlockHtml, BlockJson, BlocksHtml, ChildrenHtml, ClockSvg, HomeHtml, InputHtml,
InscriptionHtml, InscriptionJson, InscriptionsBlockHtml, InscriptionsHtml, InscriptionsJson,
OutputHtml, OutputJson, PageContent, PageHtml, PreviewAudioHtml, PreviewCodeHtml,
PreviewImageHtml, PreviewMarkdownHtml, PreviewModelHtml, PreviewPdfHtml, PreviewTextHtml,
PreviewUnknownHtml, PreviewVideoHtml, RangeHtml, RareTxt, RuneHtml, RunesHtml, SatHtml,
SatJson, TransactionHtml,
BlockHtml, BlockJson, BlocksHtml, ChildrenHtml, ClockSvg, CollectionsHtml, HomeHtml,
InputHtml, InscriptionHtml, InscriptionJson, InscriptionsBlockHtml, InscriptionsHtml,
InscriptionsJson, OutputHtml, OutputJson, PageContent, PageHtml, PreviewAudioHtml,
PreviewCodeHtml, PreviewImageHtml, PreviewMarkdownHtml, PreviewModelHtml, PreviewPdfHtml,
PreviewTextHtml, PreviewUnknownHtml, PreviewVideoHtml, RangeHtml, RareTxt, RuneHtml,
RunesHtml, SatHtml, SatJson, TransactionHtml,
},
},
axum::{
Expand Down Expand Up @@ -202,6 +202,8 @@ impl Server {
get(Self::children_paginated),
)
.route("/clock", get(Self::clock))
.route("/collections", get(Self::collections))
.route("/collections/:page", get(Self::collections_paginated))
.route("/content/:inscription_id", get(Self::content))
.route("/faq", get(Self::faq))
.route("/favicon.ico", get(Self::favicon))
Expand Down Expand Up @@ -1236,6 +1238,35 @@ impl Server {
})
}

async fn collections(
Extension(page_config): Extension<Arc<PageConfig>>,
Extension(index): Extension<Arc<Index>>,
) -> ServerResult<Response> {
Self::collections_paginated(Extension(page_config), Extension(index), Path(0)).await
}

async fn collections_paginated(
Extension(page_config): Extension<Arc<PageConfig>>,
Extension(index): Extension<Arc<Index>>,
Path(page_index): Path<usize>,
) -> ServerResult<Response> {
let (collections, more_collections) = index.get_collections_paginated(100, page_index)?;

let prev = page_index.checked_sub(1);

let next = more_collections.then_some(page_index + 1);

Ok(
CollectionsHtml {
inscriptions: collections,
prev,
next,
}
.page(page_config)
.into_response(),
)
}

async fn children(
Extension(page_config): Extension<Arc<PageConfig>>,
Extension(index): Extension<Arc<Index>>,
Expand Down Expand Up @@ -2675,6 +2706,8 @@ mod tests {
<a href=/inscriptions>Inscriptions</a>
<span>\|</span>
<a href=/blocks>Blocks</a>
<span>\|</span>
<a href=/collections>Collections</a>
</nav>
<div class=thumbnails>
<a href=/inscription/{}>.*</a>
Expand Down Expand Up @@ -3552,6 +3585,84 @@ mod tests {
);
}

#[test]
fn collections_page_prev_and_next() {
let server = TestServer::new_with_regtest_with_index_sats();

let mut parent_ids = Vec::new();

for i in 0..101 {
server.mine_blocks(1);

parent_ids.push(InscriptionId {
txid: server.bitcoin_rpc_server.broadcast_tx(TransactionTemplate {
inputs: &[(i + 1, 0, 0, inscription("text/plain", "hello").to_witness())],
..Default::default()
}),
index: 0,
});
}

for (i, parent_id) in parent_ids.iter().enumerate().take(101) {
server.mine_blocks(1);

server.bitcoin_rpc_server.broadcast_tx(TransactionTemplate {
inputs: &[
(i + 2, 1, 0, Default::default()),
(
i + 102,
0,
0,
Inscription {
content_type: Some("text/plain".into()),
body: Some("hello".into()),
parent: Some(parent_id.parent_value()),
..Default::default()
}
.to_witness(),
),
],
outputs: 2,
output_values: &[50 * COIN_VALUE, 50 * COIN_VALUE],
..Default::default()
});
}

server.mine_blocks(1);

server.assert_response_regex(
"/collections",
StatusCode::OK,
r".*
<h1>Collections</h1>
<div class=thumbnails>
<a href=/inscription/.*><iframe .* src=/preview/.*></iframe></a>
(<a href=/inscription/[[:xdigit:]]{64}i0>.*</a>\s*){99}
</div>
<div class=center>
prev
<a class=next href=/collections/1>next</a>
</div>.*"
.to_string()
.unindent(),
);

server.assert_response_regex(
"/collections/1",
StatusCode::OK,
".*
<h1>Collections</h1>
<div class=thumbnails>
<a href=/inscription/.*><iframe .* src=/preview/.*></iframe></a>
</div>
<div class=center>
<a class=prev href=/collections/0>prev</a>
next
</div>.*"
.unindent(),
);
}

#[test]
fn responses_are_gzipped() {
let server = TestServer::new();
Expand Down
2 changes: 2 additions & 0 deletions src/templates.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ pub(crate) use {
blocks::BlocksHtml,
children::ChildrenHtml,
clock::ClockSvg,
collections::CollectionsHtml,
home::HomeHtml,
iframe::Iframe,
input::InputHtml,
Expand All @@ -30,6 +31,7 @@ pub mod block;
mod blocks;
mod children;
mod clock;
pub mod collections;
mod home;
mod iframe;
mod input;
Expand Down
65 changes: 65 additions & 0 deletions src/templates/collections.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
use super::*;

#[derive(Boilerplate)]
pub(crate) struct CollectionsHtml {
pub(crate) inscriptions: Vec<InscriptionId>,
pub(crate) prev: Option<usize>,
pub(crate) next: Option<usize>,
}

impl PageContent for CollectionsHtml {
fn title(&self) -> String {
"Collections".into()
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn without_prev_and_next() {
assert_regex_match!(
CollectionsHtml {
inscriptions: vec![inscription_id(1), inscription_id(2)],
prev: None,
next: None,
},
"
<h1>Collections</h1>
<div class=thumbnails>
<a href=/inscription/1{64}i1><iframe .* src=/preview/1{64}i1></iframe></a>
<a href=/inscription/2{64}i2><iframe .* src=/preview/2{64}i2></iframe></a>
</div>
.*
prev
next
.*
"
.unindent()
);
}

#[test]
fn with_prev_and_next() {
assert_regex_match!(
CollectionsHtml {
inscriptions: vec![inscription_id(1), inscription_id(2)],
prev: Some(1),
next: Some(2),
},
"
<h1>Collections</h1>
<div class=thumbnails>
<a href=/inscription/1{64}i1><iframe .* src=/preview/1{64}i1></iframe></a>
<a href=/inscription/2{64}i2><iframe .* src=/preview/2{64}i2></iframe></a>
</div>
.*
<a class=prev href=/collections/1>prev</a>
<a class=next href=/collections/2>next</a>
.*
"
.unindent()
);
}
}
9 changes: 5 additions & 4 deletions static/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -245,15 +245,16 @@ a.mythic {
}

.tabs > a {
flex: 1;
padding: 1rem;
color: var(--light-fg);
padding: 1rem;
}

.tabs > *:nth-child(1) {
.tabs > *:first-child {
flex: 1;
text-align: right;
}

.tabs > *:nth-child(3) {
.tabs > *:last-child {
flex: 1;
text-align: left;
}
18 changes: 18 additions & 0 deletions templates/collections.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<h1>Collections</h1>
<div class=thumbnails>
%% for id in &self.inscriptions {
{{Iframe::thumbnail(*id)}}
%% }
</div>
<div class=center>
%% if let Some(prev) = self.prev {
<a class=prev href=/collections/{{prev}}>prev</a>
%% } else {
prev
%% }
%% if let Some(next) = self.next {
<a class=next href=/collections/{{next}}>next</a>
%% } else {
next
%% }
</div>
2 changes: 2 additions & 0 deletions templates/home.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
<a href=/inscriptions>Inscriptions</a>
<span>|</span>
<a href=/blocks>Blocks</a>
<span>|</span>
<a href=/collections>Collections</a>
</nav>
<div class=thumbnails>
%% for inscription in &self.inscriptions {
Expand Down

0 comments on commit b50b536

Please sign in to comment.