Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(explorer): add previous and next block navigation to block page #864

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/slow-roses-hide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'explorer': minor
---

Added previous and next block navigation to blocks page.
16 changes: 14 additions & 2 deletions apps/explorer-e2e/src/specs/block.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 }) => {
Expand Down
38 changes: 20 additions & 18 deletions apps/explorer/app/block/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 <Block block={block} blockID={id} />
return (
<Block block={block} blockID={id} currentHeight={currentTipInfo.height} />
)
}
34 changes: 33 additions & 1 deletion apps/explorer/components/Block/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
Tooltip,
EntityList,
stripPrefix,
LinkButton,
} from '@siafoundation/design-system'
import { humanNumber } from '@siafoundation/units'
import { ExplorerDatum, DatumProps } from '../ExplorerDatum'
Expand All @@ -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(
Expand All @@ -40,6 +43,9 @@ export function Block({ block, blockID }: Props) {
]
}, [block, blockID])

const previousBlockExists = block.height > 1
const nextBlockExists = block.height < currentHeight

return (
<ContentLayout
panel={
Expand All @@ -52,6 +58,32 @@ export function Block({ block, blockID }: Props) {
href={routes.block.view.replace(':id', String(block.height))}
/>
<div className="flex gap-2 items-center">
<Tooltip content="previous block" delayDuration={800}>
<LinkButton
variant={previousBlockExists ? 'gray' : 'inactive'}
href={routes.block.view.replace(
':id',
String(block.height - 1)
)}
disabled={!previousBlockExists}
data-testid="explorer-block-prevBlock"
>
<ArrowLeft16 />
</LinkButton>
</Tooltip>
<Tooltip content="next block" delayDuration={800}>
<LinkButton
variant={nextBlockExists ? 'gray' : 'inactive'}
href={routes.block.view.replace(
':id',
String(block.height + 1)
)}
disabled={!nextBlockExists}
data-testid="explorer-block-nextBlock"
>
<ArrowRight16 />
</LinkButton>
</Tooltip>
<Tooltip
content={`${humanNumber(
block.transactions?.length || 0
Expand Down
Loading