First working iteration of sender/nonce navigation
This commit is contained in:
parent
75a37f79d3
commit
47cbe36c8f
|
@ -1,5 +1,6 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { NavLink } from "react-router-dom";
|
import { NavLink } from "react-router-dom";
|
||||||
|
import { transactionURL } from "../url";
|
||||||
|
|
||||||
type TransactionLinkProps = {
|
type TransactionLinkProps = {
|
||||||
txHash: string;
|
txHash: string;
|
||||||
|
@ -8,7 +9,7 @@ type TransactionLinkProps = {
|
||||||
const TransactionLink: React.FC<TransactionLinkProps> = ({ txHash }) => (
|
const TransactionLink: React.FC<TransactionLinkProps> = ({ txHash }) => (
|
||||||
<NavLink
|
<NavLink
|
||||||
className="text-link-blue hover:text-link-blue-hover font-hash"
|
className="text-link-blue hover:text-link-blue-hover font-hash"
|
||||||
to={`/tx/${txHash}`}
|
to={transactionURL(txHash)}
|
||||||
>
|
>
|
||||||
<p className="truncate">{txHash}</p>
|
<p className="truncate">{txHash}</p>
|
||||||
</NavLink>
|
</NavLink>
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
export const MIN_API_LEVEL = 5;
|
export const MIN_API_LEVEL = 6;
|
||||||
|
|
||||||
export const PAGE_SIZE = 25;
|
export const PAGE_SIZE = 25;
|
||||||
|
|
|
@ -15,6 +15,7 @@ import BlockConfirmations from "../components/BlockConfirmations";
|
||||||
import TransactionAddress from "../components/TransactionAddress";
|
import TransactionAddress from "../components/TransactionAddress";
|
||||||
import Copy from "../components/Copy";
|
import Copy from "../components/Copy";
|
||||||
import Nonce from "../components/Nonce";
|
import Nonce from "../components/Nonce";
|
||||||
|
import NavNonce from "./NavNonce";
|
||||||
import Timestamp from "../components/Timestamp";
|
import Timestamp from "../components/Timestamp";
|
||||||
import InternalTransactionOperation from "../components/InternalTransactionOperation";
|
import InternalTransactionOperation from "../components/InternalTransactionOperation";
|
||||||
import MethodName from "../components/MethodName";
|
import MethodName from "../components/MethodName";
|
||||||
|
@ -253,6 +254,11 @@ const Details: React.FC<DetailsProps> = ({
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-baseline pl-3">
|
<div className="flex items-baseline pl-3">
|
||||||
<Nonce value={txData.nonce} />
|
<Nonce value={txData.nonce} />
|
||||||
|
<NavNonce
|
||||||
|
sender={txData.from}
|
||||||
|
nonce={txData.nonce}
|
||||||
|
latestBlockNumber={undefined}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</InfoRow>
|
</InfoRow>
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
import { NavLink } from "react-router-dom";
|
||||||
|
import { transactionURL } from "../url";
|
||||||
|
|
||||||
|
// TODO: extract common component with block/NavButton
|
||||||
|
type NavButtonProps = {
|
||||||
|
txHash: string | undefined;
|
||||||
|
disabled?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
const NavButton: React.FC<NavButtonProps> = ({
|
||||||
|
txHash,
|
||||||
|
disabled,
|
||||||
|
children,
|
||||||
|
}) => {
|
||||||
|
if (disabled || txHash === undefined) {
|
||||||
|
return (
|
||||||
|
<span className="bg-link-blue bg-opacity-10 text-gray-400 rounded px-2 py-1 text-xs">
|
||||||
|
{children}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<NavLink
|
||||||
|
className="transition-colors bg-link-blue bg-opacity-10 text-link-blue hover:bg-opacity-100 hover:text-white disabled:bg-link-blue disabled:text-gray-400 disabled:cursor-default rounded px-2 py-1 text-xs"
|
||||||
|
to={transactionURL(txHash)}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</NavLink>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default NavButton;
|
|
@ -0,0 +1,56 @@
|
||||||
|
import React, { useContext } from "react";
|
||||||
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
|
import { faChevronLeft } from "@fortawesome/free-solid-svg-icons/faChevronLeft";
|
||||||
|
import { faChevronRight } from "@fortawesome/free-solid-svg-icons/faChevronRight";
|
||||||
|
import NavButton from "./NavButton";
|
||||||
|
import { ChecksummedAddress } from "../types";
|
||||||
|
import { RuntimeContext } from "../useRuntime";
|
||||||
|
import { useTransactionBySenderAndNonce } from "../useErigonHooks";
|
||||||
|
|
||||||
|
type NavNonceProps = {
|
||||||
|
sender: ChecksummedAddress;
|
||||||
|
nonce: number;
|
||||||
|
latestBlockNumber: number | undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
const NavNonce: React.FC<NavNonceProps> = ({
|
||||||
|
sender,
|
||||||
|
nonce,
|
||||||
|
latestBlockNumber,
|
||||||
|
}) => {
|
||||||
|
const { provider } = useContext(RuntimeContext);
|
||||||
|
const prevTxHash = useTransactionBySenderAndNonce(
|
||||||
|
provider,
|
||||||
|
sender,
|
||||||
|
nonce - 1
|
||||||
|
);
|
||||||
|
const nextTxHash = useTransactionBySenderAndNonce(
|
||||||
|
provider,
|
||||||
|
sender,
|
||||||
|
nonce + 1
|
||||||
|
);
|
||||||
|
const lastTxHash = nextTxHash;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="pl-2 self-center flex space-x-1">
|
||||||
|
<NavButton txHash={prevTxHash} disabled={nonce === 0}>
|
||||||
|
<FontAwesomeIcon icon={faChevronLeft} />
|
||||||
|
</NavButton>
|
||||||
|
<NavButton
|
||||||
|
txHash={nextTxHash}
|
||||||
|
disabled={latestBlockNumber === undefined || nonce >= latestBlockNumber}
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon icon={faChevronRight} />
|
||||||
|
</NavButton>
|
||||||
|
<NavButton
|
||||||
|
txHash={lastTxHash}
|
||||||
|
disabled={latestBlockNumber === undefined || nonce >= latestBlockNumber}
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon icon={faChevronRight} />
|
||||||
|
<FontAwesomeIcon icon={faChevronRight} />
|
||||||
|
</NavButton>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default React.memo(NavNonce);
|
|
@ -18,6 +18,8 @@ export const blockURL = (blockNum: BlockTag) => `/block/${blockNum}`;
|
||||||
|
|
||||||
export const blockTxsURL = (blockNum: BlockTag) => `/block/${blockNum}/txs`;
|
export const blockTxsURL = (blockNum: BlockTag) => `/block/${blockNum}/txs`;
|
||||||
|
|
||||||
|
export const transactionURL = (txHash: string) => `/tx/${txHash}`;
|
||||||
|
|
||||||
export enum SourcifySource {
|
export enum SourcifySource {
|
||||||
// Resolve trusted IPNS for root IPFS
|
// Resolve trusted IPNS for root IPFS
|
||||||
IPFS_IPNS,
|
IPFS_IPNS,
|
||||||
|
|
|
@ -502,3 +502,43 @@ export const useTransactionError = (
|
||||||
|
|
||||||
return [errorMsg, data, isCustomError];
|
return [errorMsg, data, isCustomError];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const useTransactionBySenderAndNonce = (
|
||||||
|
provider: JsonRpcProvider | undefined,
|
||||||
|
sender: ChecksummedAddress | undefined,
|
||||||
|
nonce: number | undefined
|
||||||
|
): string | undefined => {
|
||||||
|
const [txHash, setTxHash] = useState<string | undefined>();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// Reset
|
||||||
|
setTxHash(undefined);
|
||||||
|
|
||||||
|
if (
|
||||||
|
provider === undefined ||
|
||||||
|
sender === undefined ||
|
||||||
|
nonce === undefined ||
|
||||||
|
nonce < 0
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const readTxHash = async () => {
|
||||||
|
const result = (await provider.send(
|
||||||
|
"ots_getTransactionBySenderAndNonce",
|
||||||
|
[sender, nonce]
|
||||||
|
)) as string;
|
||||||
|
|
||||||
|
// Empty or success
|
||||||
|
if (result === "0x") {
|
||||||
|
setTxHash(undefined);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setTxHash(result);
|
||||||
|
};
|
||||||
|
readTxHash();
|
||||||
|
}, [provider, sender, nonce]);
|
||||||
|
|
||||||
|
return txHash;
|
||||||
|
};
|
||||||
|
|
Loading…
Reference in New Issue