Explorer: fix slot links and block details page on devnet (#13274)

This commit is contained in:
Justin Starry 2020-10-29 20:34:05 +08:00 committed by GitHub
parent c74c565d25
commit 0f05e086fe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 44 additions and 39 deletions

View File

@ -6,32 +6,31 @@ import { Signature } from "components/common/Signature";
import { ErrorCard } from "components/common/ErrorCard"; import { ErrorCard } from "components/common/ErrorCard";
import { LoadingCard } from "components/common/LoadingCard"; import { LoadingCard } from "components/common/LoadingCard";
import { Slot } from "components/common/Slot"; import { Slot } from "components/common/Slot";
import { ClusterStatus, useCluster } from "providers/cluster";
export function BlockHistoryCard({ slot }: { slot: number }) { export function BlockHistoryCard({ slot }: { slot: number }) {
const confirmedBlock = useBlock(slot); const confirmedBlock = useBlock(slot);
const fetchBlock = useFetchBlock(); const fetchBlock = useFetchBlock();
const { status } = useCluster();
const refresh = () => fetchBlock(slot); const refresh = () => fetchBlock(slot);
// Fetch block on load
React.useEffect(() => { React.useEffect(() => {
if (!confirmedBlock) refresh(); if (!confirmedBlock && status === ClusterStatus.Connected) refresh();
}, [confirmedBlock, slot]); // eslint-disable-line react-hooks/exhaustive-deps }, [slot, status]); // eslint-disable-line react-hooks/exhaustive-deps
if (!confirmedBlock) {
return null;
}
if (confirmedBlock.data === undefined) {
if (confirmedBlock.status === FetchStatus.Fetching) {
return <LoadingCard message="Loading block" />;
}
if (!confirmedBlock || confirmedBlock.status === FetchStatus.Fetching) {
return <LoadingCard message="Loading block" />;
} else if (
confirmedBlock.data === undefined ||
confirmedBlock.status === FetchStatus.FetchFailed
) {
return <ErrorCard retry={refresh} text="Failed to fetch block" />; return <ErrorCard retry={refresh} text="Failed to fetch block" />;
} else if (confirmedBlock.data.block === undefined) {
return <ErrorCard retry={refresh} text={`Block ${slot} was not found`} />;
} }
if (confirmedBlock.status === FetchStatus.FetchFailed) { const block = confirmedBlock.data.block;
return <ErrorCard retry={refresh} text="Failed to fetch block" />;
}
return ( return (
<> <>
<div className="card"> <div className="card">
@ -44,31 +43,31 @@ export function BlockHistoryCard({ slot }: { slot: number }) {
<tr> <tr>
<td className="w-100">Slot</td> <td className="w-100">Slot</td>
<td className="text-lg-right text-monospace"> <td className="text-lg-right text-monospace">
<Slot slot={Number(slot)} /> <Slot slot={slot} />
</td> </td>
</tr> </tr>
<tr> <tr>
<td className="w-100">Parent Slot</td> <td className="w-100">Parent Slot</td>
<td className="text-lg-right text-monospace"> <td className="text-lg-right text-monospace">
<Slot slot={confirmedBlock.data.parentSlot} link /> <Slot slot={block.parentSlot} link />
</td> </td>
</tr> </tr>
<tr> <tr>
<td className="w-100">Blockhash</td> <td className="w-100">Blockhash</td>
<td className="text-lg-right text-monospace"> <td className="text-lg-right text-monospace">
<span>{confirmedBlock.data.blockhash}</span> <span>{block.blockhash}</span>
</td> </td>
</tr> </tr>
<tr> <tr>
<td className="w-100">Previous Blockhash</td> <td className="w-100">Previous Blockhash</td>
<td className="text-lg-right text-monospace"> <td className="text-lg-right text-monospace">
<span>{confirmedBlock.data.previousBlockhash}</span> <span>{block.previousBlockhash}</span>
</td> </td>
</tr> </tr>
</TableCardBody> </TableCardBody>
</div> </div>
{confirmedBlock.data.transactions.length === 0 ? ( {block.transactions.length === 0 ? (
<ErrorCard text="This block has no transactions" /> <ErrorCard text="This block has no transactions" />
) : ( ) : (
<div className="card"> <div className="card">
@ -85,7 +84,7 @@ export function BlockHistoryCard({ slot }: { slot: number }) {
</tr> </tr>
</thead> </thead>
<tbody className="list"> <tbody className="list">
{confirmedBlock.data.transactions.map((tx, i) => { {block.transactions.map((tx, i) => {
let statusText; let statusText;
let statusClass; let statusClass;
let signature: React.ReactNode; let signature: React.ReactNode;

View File

@ -1,5 +1,6 @@
import React, { useState } from "react"; import React, { useState } from "react";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import { clusterPath } from "utils/url";
type CopyState = "copy" | "copied"; type CopyState = "copy" | "copied";
type Props = { type Props = {
@ -30,7 +31,7 @@ export function Slot({ slot, link }: Props) {
return link ? ( return link ? (
<span className="text-monospace"> <span className="text-monospace">
{copyButton} {copyButton}
<Link className="" to={`/block/${slot}`}> <Link to={clusterPath(`/block/${slot}`)}>
{slot.toLocaleString("en-US")} {slot.toLocaleString("en-US")}
</Link> </Link>
</span> </span>

View File

@ -15,8 +15,12 @@ export enum ActionType {
Clear, Clear,
} }
type State = Cache.State<ConfirmedBlock>; type Block = {
type Dispatch = Cache.Dispatch<ConfirmedBlock>; block?: ConfirmedBlock;
};
type State = Cache.State<Block>;
type Dispatch = Cache.Dispatch<Block>;
const StateContext = React.createContext<State | undefined>(undefined); const StateContext = React.createContext<State | undefined>(undefined);
const DispatchContext = React.createContext<Dispatch | undefined>(undefined); const DispatchContext = React.createContext<Dispatch | undefined>(undefined);
@ -25,7 +29,7 @@ type BlockProviderProps = { children: React.ReactNode };
export function BlockProvider({ children }: BlockProviderProps) { export function BlockProvider({ children }: BlockProviderProps) {
const { url } = useCluster(); const { url } = useCluster();
const [state, dispatch] = Cache.useReducer<ConfirmedBlock>(url); const [state, dispatch] = Cache.useReducer<Block>(url);
React.useEffect(() => { React.useEffect(() => {
dispatch({ type: ActionType.Clear, url }); dispatch({ type: ActionType.Clear, url });
@ -40,9 +44,7 @@ export function BlockProvider({ children }: BlockProviderProps) {
); );
} }
export function useBlock( export function useBlock(key: number): Cache.CacheEntry<Block> | undefined {
key: number
): Cache.CacheEntry<ConfirmedBlock> | undefined {
const context = React.useContext(StateContext); const context = React.useContext(StateContext);
if (!context) { if (!context) {
@ -66,18 +68,23 @@ export async function fetchBlock(
}); });
let status: FetchStatus; let status: FetchStatus;
let data: ConfirmedBlock | undefined; let data: Block | undefined = undefined;
try { try {
const connection = new Connection(url, "max"); const connection = new Connection(url, "max");
data = await connection.getConfirmedBlock(Number(key)); data = { block: await connection.getConfirmedBlock(Number(key)) };
status = FetchStatus.Fetched; status = FetchStatus.Fetched;
} catch (error) { } catch (err) {
console.log(error); const error = err as Error;
if (cluster !== Cluster.Custom) { if (error.message.includes("not found")) {
Sentry.captureException(error, { tags: { url } }); data = {} as Block;
status = FetchStatus.Fetched;
} else {
status = FetchStatus.FetchFailed;
if (cluster !== Cluster.Custom) {
Sentry.captureException(error, { tags: { url } });
}
} }
status = FetchStatus.FetchFailed;
} }
dispatch({ dispatch({
@ -90,14 +97,12 @@ export async function fetchBlock(
} }
export function useFetchBlock() { export function useFetchBlock() {
const { cluster, url } = useCluster();
const state = React.useContext(StateContext);
const dispatch = React.useContext(DispatchContext); const dispatch = React.useContext(DispatchContext);
if (!dispatch) {
if (!state || !dispatch) {
throw new Error(`useFetchBlock must be used within a BlockProvider`); throw new Error(`useFetchBlock must be used within a BlockProvider`);
} }
const { cluster, url } = useCluster();
return React.useCallback( return React.useCallback(
(key: number) => fetchBlock(dispatch, url, cluster, key), (key: number) => fetchBlock(dispatch, url, cluster, key),
[dispatch, cluster, url] [dispatch, cluster, url]