Split address page routing
This commit is contained in:
parent
f0c325cbf4
commit
5e5dc6f20a
183
src/Address.tsx
183
src/Address.tsx
@ -1,183 +1,28 @@
|
||||
import React, { useEffect, useContext, useCallback } from "react";
|
||||
import {
|
||||
useParams,
|
||||
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";
|
||||
import React from "react";
|
||||
import { useSearchParams } from "react-router-dom";
|
||||
import AddressMainPage from "./AddressMainPage";
|
||||
|
||||
const AddressTransactionByNonce = React.lazy(
|
||||
() => 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 { 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
|
||||
const [searchParams] = useSearchParams();
|
||||
const rawNonce = searchParams.get("nonce");
|
||||
if (rawNonce !== null) {
|
||||
return (
|
||||
<AddressTransactionByNonce
|
||||
checksummedAddress={checksummedAddress}
|
||||
rawNonce={rawNonce}
|
||||
/>
|
||||
);
|
||||
return <AddressTransactionByNonce rawNonce={rawNonce} />;
|
||||
}
|
||||
|
||||
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>
|
||||
);
|
||||
// Standard address main page with tabs
|
||||
return <AddressMainPage />;
|
||||
};
|
||||
|
||||
export default Address;
|
||||
|
171
src/AddressMainPage.tsx
Normal file
171
src/AddressMainPage.tsx
Normal 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;
|
@ -1,24 +1,46 @@
|
||||
import React, { useContext, useEffect, useState } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import React, { useCallback, useContext, useEffect, useState } from "react";
|
||||
import { useNavigate, useParams, useSearchParams } from "react-router-dom";
|
||||
import StandardFrame from "./StandardFrame";
|
||||
import AddressOrENSNameInvalidNonce from "./components/AddressOrENSNameInvalidNonce";
|
||||
import AddressOrENSNameNoTx from "./components/AddressOrENSNameNoTx";
|
||||
import { ChecksummedAddress } from "./types";
|
||||
import { transactionURL } from "./url";
|
||||
import { useTransactionBySenderAndNonce } from "./useErigonHooks";
|
||||
import { RuntimeContext } from "./useRuntime";
|
||||
import { useAddressOrENS } from "./useResolvedAddresses";
|
||||
import { ChecksummedAddress } from "./types";
|
||||
import { transactionURL } from "./url";
|
||||
|
||||
type AddressTransactionByNonceProps = {
|
||||
checksummedAddress: ChecksummedAddress | undefined;
|
||||
rawNonce: string;
|
||||
};
|
||||
|
||||
const AddressTransactionByNonce: React.FC<AddressTransactionByNonceProps> = ({
|
||||
checksummedAddress,
|
||||
rawNonce,
|
||||
}) => {
|
||||
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
|
||||
const [txCount, setTxCount] = useState<number | undefined>();
|
||||
useEffect(() => {
|
||||
@ -54,7 +76,6 @@ const AddressTransactionByNonce: React.FC<AddressTransactionByNonceProps> = ({
|
||||
checksummedAddress,
|
||||
nonce !== undefined && isNaN(nonce) ? undefined : nonce
|
||||
);
|
||||
const navigate = useNavigate();
|
||||
|
||||
// Loading...
|
||||
if (
|
||||
|
Loading…
Reference in New Issue
Block a user