Split address page routing

This commit is contained in:
Willian Mitsuda 2022-08-22 19:17:04 -03:00
parent f0c325cbf4
commit 5e5dc6f20a
No known key found for this signature in database
3 changed files with 213 additions and 176 deletions

View File

@ -1,183 +1,28 @@
import React, { useEffect, useContext, useCallback } from "react"; import React from "react";
import { import { useSearchParams } from "react-router-dom";
useParams, import AddressMainPage from "./AddressMainPage";
useNavigate,
Routes,
Route,
useSearchParams,
} from "react-router-dom";
import { Tab } from "@headlessui/react";
import Blockies from "react-blockies";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faCircleNotch } from "@fortawesome/free-solid-svg-icons/faCircleNotch";
import { faQuestionCircle } from "@fortawesome/free-regular-svg-icons/faQuestionCircle";
import StandardFrame from "./StandardFrame";
import StandardSubtitle from "./StandardSubtitle";
import AddressOrENSNameNotFound from "./components/AddressOrENSNameNotFound";
import Copy from "./components/Copy";
import Faucet from "./components/Faucet";
import NavTab from "./components/NavTab";
import SourcifyLogo from "./sourcify/SourcifyLogo";
import AddressTransactionResults from "./address/AddressTransactionResults";
import Contracts from "./address/Contracts";
import { RuntimeContext } from "./useRuntime";
import { useAddressOrENS } from "./useResolvedAddresses";
import { useSourcifyMetadata } from "./sourcify/useSourcify";
import { ChecksummedAddress } from "./types";
import { useHasCode } from "./useErigonHooks";
import { useChainInfo } from "./useChainInfo";
const AddressTransactionByNonce = React.lazy( const AddressTransactionByNonce = React.lazy(
() => import("./AddressTransactionByNonce") () => import("./AddressTransactionByNonce")
); );
/**
* This is the default handler for /address/* URL path.
*
* It can redirect to different child components depending on search
* query params, so it is not possible to use default path routing
* mechanisms to declarative-model them.
*/
const Address: React.FC = () => { const Address: React.FC = () => {
const { provider } = useContext(RuntimeContext);
const { addressOrName, direction } = useParams();
if (addressOrName === undefined) {
throw new Error("addressOrName couldn't be undefined here");
}
const navigate = useNavigate();
const [searchParams] = useSearchParams();
const urlFixer = useCallback(
(address: ChecksummedAddress) => {
navigate(
`/address/${address}${
direction ? "/" + direction : ""
}?${searchParams.toString()}`,
{ replace: true }
);
},
[navigate, direction, searchParams]
);
const [checksummedAddress, isENS, error] = useAddressOrENS(
addressOrName,
urlFixer
);
useEffect(() => {
if (isENS || checksummedAddress === undefined) {
document.title = `Address ${addressOrName} | Otterscan`;
} else {
document.title = `Address ${checksummedAddress} | Otterscan`;
}
}, [addressOrName, checksummedAddress, isENS]);
const hasCode = useHasCode(provider, checksummedAddress, "latest");
const addressMetadata = useSourcifyMetadata(
hasCode ? checksummedAddress : undefined,
provider?.network.chainId
);
const { network, faucets } = useChainInfo();
// Search address by nonce === transaction @ nonce // Search address by nonce === transaction @ nonce
const [searchParams] = useSearchParams();
const rawNonce = searchParams.get("nonce"); const rawNonce = searchParams.get("nonce");
if (rawNonce !== null) { if (rawNonce !== null) {
return ( return <AddressTransactionByNonce rawNonce={rawNonce} />;
<AddressTransactionByNonce
checksummedAddress={checksummedAddress}
rawNonce={rawNonce}
/>
);
} }
return ( // Standard address main page with tabs
<StandardFrame> return <AddressMainPage />;
{error ? (
<AddressOrENSNameNotFound
addressOrENSName={addressOrName}
supportsENS={provider?.network.ensAddress !== undefined}
/>
) : (
checksummedAddress && (
<>
<StandardSubtitle>
<div className="flex space-x-2 items-baseline">
<Blockies
className="self-center rounded"
seed={checksummedAddress.toLowerCase()}
scale={3}
/>
<span>Address</span>
<span className="font-address text-base text-gray-500">
{checksummedAddress}
</span>
<Copy value={checksummedAddress} rounded />
{/* Only display faucets for testnets who actually have any */}
{network === "testnet" && faucets && faucets.length > 0 && (
<Faucet address={checksummedAddress} rounded />
)}
{isENS && (
<span className="rounded-lg px-2 py-1 bg-gray-200 text-gray-500 text-xs">
ENS: {addressOrName}
</span>
)}
</div>
</StandardSubtitle>
<Tab.Group>
<Tab.List className="flex space-x-2 border-l border-r border-t rounded-t-lg bg-white">
<NavTab href={`/address/${addressOrName}`}>Overview</NavTab>
{hasCode && (
<NavTab href={`/address/${addressOrName}/contract`}>
<span
className={`flex items-baseline space-x-2 ${
addressMetadata === undefined ? "italic opacity-50" : ""
}`}
>
<span>Contract</span>
{addressMetadata === undefined ? (
<span className="self-center">
<FontAwesomeIcon
className="animate-spin"
icon={faCircleNotch}
/>
</span>
) : addressMetadata === null ? (
<span className="self-center text-red-500">
<FontAwesomeIcon icon={faQuestionCircle} />
</span>
) : (
<span className="self-center">
<SourcifyLogo />
</span>
)}
</span>
</NavTab>
)}
</Tab.List>
<Tab.Panels>
<Routes>
<Route
index
element={
<AddressTransactionResults address={checksummedAddress} />
}
/>
<Route
path="txs/:direction"
element={
<AddressTransactionResults address={checksummedAddress} />
}
/>
<Route
path="contract"
element={
<Contracts
checksummedAddress={checksummedAddress}
rawMetadata={addressMetadata}
/>
}
/>
</Routes>
</Tab.Panels>
</Tab.Group>
</>
)
)}
</StandardFrame>
);
}; };
export default Address; export default Address;

171
src/AddressMainPage.tsx Normal file
View File

@ -0,0 +1,171 @@
import React, { useEffect, useCallback, useContext } from "react";
import {
Routes,
Route,
useNavigate,
useParams,
useSearchParams,
} from "react-router-dom";
import { Tab } from "@headlessui/react";
import Blockies from "react-blockies";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faCircleNotch } from "@fortawesome/free-solid-svg-icons/faCircleNotch";
import { faQuestionCircle } from "@fortawesome/free-regular-svg-icons/faQuestionCircle";
import StandardFrame from "./StandardFrame";
import StandardSubtitle from "./StandardSubtitle";
import AddressOrENSNameNotFound from "./components/AddressOrENSNameNotFound";
import Copy from "./components/Copy";
import Faucet from "./components/Faucet";
import NavTab from "./components/NavTab";
import SourcifyLogo from "./sourcify/SourcifyLogo";
import AddressTransactionResults from "./address/AddressTransactionResults";
import Contracts from "./address/Contracts";
import { RuntimeContext } from "./useRuntime";
import { useHasCode } from "./useErigonHooks";
import { useChainInfo } from "./useChainInfo";
import { useAddressOrENS } from "./useResolvedAddresses";
import { useSourcifyMetadata } from "./sourcify/useSourcify";
import { ChecksummedAddress } from "./types";
type AddressMainPageProps = {};
const AddressMainPage: React.FC<AddressMainPageProps> = ({}) => {
const { provider } = useContext(RuntimeContext);
const { addressOrName, direction } = useParams();
if (addressOrName === undefined) {
throw new Error("addressOrName couldn't be undefined here");
}
const navigate = useNavigate();
const [searchParams] = useSearchParams();
const urlFixer = useCallback(
(address: ChecksummedAddress) => {
navigate(
`/address/${address}${
direction ? "/" + direction : ""
}?${searchParams.toString()}`,
{ replace: true }
);
},
[navigate, direction, searchParams]
);
const [checksummedAddress, isENS, error] = useAddressOrENS(
addressOrName,
urlFixer
);
const hasCode = useHasCode(provider, checksummedAddress, "latest");
const addressMetadata = useSourcifyMetadata(
hasCode ? checksummedAddress : undefined,
provider?.network.chainId
);
const { network, faucets } = useChainInfo();
useEffect(() => {
if (isENS || checksummedAddress === undefined) {
document.title = `Address ${addressOrName} | Otterscan`;
} else {
document.title = `Address ${checksummedAddress} | Otterscan`;
}
}, [addressOrName, checksummedAddress, isENS]);
return (
<StandardFrame>
{error ? (
<AddressOrENSNameNotFound
addressOrENSName={addressOrName}
supportsENS={provider?.network.ensAddress !== undefined}
/>
) : (
checksummedAddress && (
<>
<StandardSubtitle>
<div className="flex space-x-2 items-baseline">
<Blockies
className="self-center rounded"
seed={checksummedAddress.toLowerCase()}
scale={3}
/>
<span>Address</span>
<span className="font-address text-base text-gray-500">
{checksummedAddress}
</span>
<Copy value={checksummedAddress} rounded />
{/* Only display faucets for testnets who actually have any */}
{network === "testnet" && faucets && faucets.length > 0 && (
<Faucet address={checksummedAddress} rounded />
)}
{isENS && (
<span className="rounded-lg px-2 py-1 bg-gray-200 text-gray-500 text-xs">
ENS: {addressOrName}
</span>
)}
</div>
</StandardSubtitle>
<Tab.Group>
<Tab.List className="flex space-x-2 border-l border-r border-t rounded-t-lg bg-white">
<NavTab href={`/address/${addressOrName}`}>Overview</NavTab>
{hasCode && (
<NavTab href={`/address/${addressOrName}/contract`}>
<span
className={`flex items-baseline space-x-2 ${
addressMetadata === undefined ? "italic opacity-50" : ""
}`}
>
<span>Contract</span>
{addressMetadata === undefined ? (
<span className="self-center">
<FontAwesomeIcon
className="animate-spin"
icon={faCircleNotch}
/>
</span>
) : addressMetadata === null ? (
<span className="self-center text-red-500">
<FontAwesomeIcon icon={faQuestionCircle} />
</span>
) : (
<span className="self-center">
<SourcifyLogo />
</span>
)}
</span>
</NavTab>
)}
</Tab.List>
<Tab.Panels>
<Routes>
<Route
index
element={
<AddressTransactionResults address={checksummedAddress} />
}
/>
<Route
path="txs/:direction"
element={
<AddressTransactionResults address={checksummedAddress} />
}
/>
<Route
path="contract"
element={
<Contracts
checksummedAddress={checksummedAddress}
rawMetadata={addressMetadata}
/>
}
/>
</Routes>
</Tab.Panels>
</Tab.Group>
</>
)
)}
</StandardFrame>
);
};
export default AddressMainPage;

View File

@ -1,24 +1,46 @@
import React, { useContext, useEffect, useState } from "react"; import React, { useCallback, useContext, useEffect, useState } from "react";
import { useNavigate } from "react-router-dom"; import { useNavigate, useParams, useSearchParams } from "react-router-dom";
import StandardFrame from "./StandardFrame"; import StandardFrame from "./StandardFrame";
import AddressOrENSNameInvalidNonce from "./components/AddressOrENSNameInvalidNonce"; import AddressOrENSNameInvalidNonce from "./components/AddressOrENSNameInvalidNonce";
import AddressOrENSNameNoTx from "./components/AddressOrENSNameNoTx"; import AddressOrENSNameNoTx from "./components/AddressOrENSNameNoTx";
import { ChecksummedAddress } from "./types";
import { transactionURL } from "./url";
import { useTransactionBySenderAndNonce } from "./useErigonHooks"; import { useTransactionBySenderAndNonce } from "./useErigonHooks";
import { RuntimeContext } from "./useRuntime"; import { RuntimeContext } from "./useRuntime";
import { useAddressOrENS } from "./useResolvedAddresses";
import { ChecksummedAddress } from "./types";
import { transactionURL } from "./url";
type AddressTransactionByNonceProps = { type AddressTransactionByNonceProps = {
checksummedAddress: ChecksummedAddress | undefined;
rawNonce: string; rawNonce: string;
}; };
const AddressTransactionByNonce: React.FC<AddressTransactionByNonceProps> = ({ const AddressTransactionByNonce: React.FC<AddressTransactionByNonceProps> = ({
checksummedAddress,
rawNonce, rawNonce,
}) => { }) => {
const { provider } = useContext(RuntimeContext); const { provider } = useContext(RuntimeContext);
const { addressOrName, direction } = useParams();
if (addressOrName === undefined) {
throw new Error("addressOrName couldn't be undefined here");
}
const navigate = useNavigate();
const [searchParams] = useSearchParams();
const urlFixer = useCallback(
(address: ChecksummedAddress) => {
navigate(
`/address/${address}${
direction ? "/" + direction : ""
}?${searchParams.toString()}`,
{ replace: true }
);
},
[navigate, direction, searchParams]
);
const [checksummedAddress, isENS, error] = useAddressOrENS(
addressOrName,
urlFixer
);
// Calculate txCount ONLY when asked for latest nonce // Calculate txCount ONLY when asked for latest nonce
const [txCount, setTxCount] = useState<number | undefined>(); const [txCount, setTxCount] = useState<number | undefined>();
useEffect(() => { useEffect(() => {
@ -54,7 +76,6 @@ const AddressTransactionByNonce: React.FC<AddressTransactionByNonceProps> = ({
checksummedAddress, checksummedAddress,
nonce !== undefined && isNaN(nonce) ? undefined : nonce nonce !== undefined && isNaN(nonce) ? undefined : nonce
); );
const navigate = useNavigate();
// Loading... // Loading...
if ( if (