diff --git a/.changeset/slow-roses-hide.md b/.changeset/slow-roses-hide.md new file mode 100644 index 000000000..ecce31ba1 --- /dev/null +++ b/.changeset/slow-roses-hide.md @@ -0,0 +1,5 @@ +--- +'explorer': minor +--- + +Added previous and next block navigation to blocks page. diff --git a/apps/explorer-e2e/src/specs/block.spec.ts b/apps/explorer-e2e/src/specs/block.spec.ts index c1e746a0e..ca5ad1622 100644 --- a/apps/explorer-e2e/src/specs/block.spec.ts +++ b/apps/explorer-e2e/src/specs/block.spec.ts @@ -28,10 +28,22 @@ test('block can be directly navigated to by height', async ({ page }) => { await expect(page.getByText(TEST_BLOCK_1.display.title).nth(0)).toBeVisible() }) -test('block can be directly navigated to by id', async ({ page }) => { +test('block can navigate to previous block', async ({ page }) => { await explorerApp.goTo('/block/' + TEST_BLOCK_1.id) + await page.getByTestId('explorer-block-prevBlock').click() - await expect(page.getByText(TEST_BLOCK_1.display.title).nth(0)).toBeVisible() + await expect( + page.getByText((Number(TEST_BLOCK_1.height) - 1).toLocaleString()) + ).toBeVisible() +}) + +test('block can navigate to nextblock', async ({ page }) => { + await explorerApp.goTo('/block/' + TEST_BLOCK_1.id) + await page.getByTestId('explorer-block-nextBlock').click() + + await expect( + page.getByText((Number(TEST_BLOCK_1.height) + 1).toLocaleString()) + ).toBeVisible() }) test('block can click through to a transaction', async ({ page }) => { diff --git a/apps/explorer/app/block/[id]/page.tsx b/apps/explorer/app/block/[id]/page.tsx index 52dde558b..bcb6b3436 100644 --- a/apps/explorer/app/block/[id]/page.tsx +++ b/apps/explorer/app/block/[id]/page.tsx @@ -37,31 +37,33 @@ export default async function Page({ params }) { // Check if the incoming id is referencing height. if (!isNaN(Number(params?.id))) { - // If it is, we need the block ID. - - // Grab the tip at this height. - const [tipInfo, tipInfoError] = await to( + // If it is, we need the block ID at that height. + const [tipAtHeightInfo, tipAtHeightInfoError] = await to( explored.consensusTipByHeight({ params: { height: params?.id } }) ) + if (tipAtHeightInfoError) throw tipAtHeightInfoError + if (!tipAtHeightInfo) throw notFound() - if (tipInfoError) throw tipInfoError - if (!tipInfo) throw notFound() - - id = tipInfo.id + id = tipAtHeightInfo.id } else { - // If it is not the height, it is referencing the ID. No call necessary. + // If it is not the height, assume we're referencing ID. No call necessary. id = params?.id } - // Get the block using the id from the previous request. - const [block, blockError] = await to(explored.blockByID({ params: { id } })) + // Get the block using the id from the previous sequence. Also grab the + // currentTip for next block navigation handling. + const [[block, blockError], [currentTipInfo, currentTipInfoError]] = + await Promise.all([ + to(explored.blockByID({ params: { id } })), + to(explored.consensusTip()), + ]) - if (blockError) { - throw blockError - } - if (!block) { - return notFound() - } + if (blockError) throw blockError + if (currentTipInfoError) throw currentTipInfoError + if (!block) return notFound() + if (!currentTipInfo) throw notFound() - return + return ( + + ) } diff --git a/apps/explorer/components/Block/index.tsx b/apps/explorer/components/Block/index.tsx index ee821a510..0d90677e8 100644 --- a/apps/explorer/components/Block/index.tsx +++ b/apps/explorer/components/Block/index.tsx @@ -3,6 +3,7 @@ import { Tooltip, EntityList, stripPrefix, + LinkButton, } from '@siafoundation/design-system' import { humanNumber } from '@siafoundation/units' import { ExplorerDatum, DatumProps } from '../ExplorerDatum' @@ -11,13 +12,15 @@ import { routes } from '../../config/routes' import { EntityHeading } from '../EntityHeading' import { ContentLayout } from '../ContentLayout' import { ExplorerBlock } from '@siafoundation/explored-types' +import { ArrowLeft16, ArrowRight16 } from '@siafoundation/react-icons' type Props = { block: ExplorerBlock blockID: string + currentHeight: number } -export function Block({ block, blockID }: Props) { +export function Block({ block, blockID, currentHeight }: Props) { const blockDatums: DatumProps[] = useMemo(() => { // Grab the miner payout address const minerPayoutAddress = block.minerPayouts.find( @@ -40,6 +43,9 @@ export function Block({ block, blockID }: Props) { ] }, [block, blockID]) + const previousBlockExists = block.height > 1 + const nextBlockExists = block.height < currentHeight + return (
+ + + + + + + + + +