Fix false negative not found response
This commit is contained in:
parent
e31b30c595
commit
b68dc5cf85
|
@ -2,12 +2,10 @@ import React from "react";
|
|||
import bs58 from "bs58";
|
||||
import {
|
||||
Source,
|
||||
useFetchTransactionStatus,
|
||||
useTransactionStatus,
|
||||
useTransactionDetails,
|
||||
useTransactionsDispatch,
|
||||
useDetailsDispatch,
|
||||
checkTransactionStatus,
|
||||
ActionType,
|
||||
FetchStatus
|
||||
} from "../providers/transactions";
|
||||
import { fetchDetails } from "providers/transactions/details";
|
||||
|
@ -28,8 +26,7 @@ import { useHistory, useLocation } from "react-router-dom";
|
|||
|
||||
type Props = { signature: TransactionSignature };
|
||||
export default function TransactionDetails({ signature }: Props) {
|
||||
const dispatch = useTransactionsDispatch();
|
||||
const { url } = useCluster();
|
||||
const fetchTransaction = useFetchTransactionStatus();
|
||||
const [, setShow] = useClusterModal();
|
||||
const [search, setSearch] = React.useState(signature);
|
||||
const history = useHistory();
|
||||
|
@ -41,13 +38,8 @@ export default function TransactionDetails({ signature }: Props) {
|
|||
|
||||
// Fetch transaction on load
|
||||
React.useEffect(() => {
|
||||
dispatch({
|
||||
type: ActionType.FetchSignature,
|
||||
signature,
|
||||
source: Source.Url
|
||||
});
|
||||
checkTransactionStatus(dispatch, signature, url);
|
||||
}, [signature, dispatch, url]);
|
||||
fetchTransaction(signature, Source.Url);
|
||||
}, [signature]); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
|
||||
const searchInput = (
|
||||
<input
|
||||
|
@ -104,20 +96,15 @@ export default function TransactionDetails({ signature }: Props) {
|
|||
|
||||
function StatusCard({ signature }: Props) {
|
||||
const status = useTransactionStatus(signature);
|
||||
const dispatch = useTransactionsDispatch();
|
||||
const refresh = useFetchTransactionStatus();
|
||||
const details = useTransactionDetails(signature);
|
||||
const { url } = useCluster();
|
||||
|
||||
const refreshStatus = () => {
|
||||
checkTransactionStatus(dispatch, signature, url);
|
||||
};
|
||||
|
||||
if (!status || status.fetchStatus === FetchStatus.Fetching) {
|
||||
return <LoadingCard />;
|
||||
} else if (status?.fetchStatus === FetchStatus.FetchFailed) {
|
||||
return <RetryCard retry={refreshStatus} text="Fetch Failed" />;
|
||||
return <RetryCard retry={() => refresh(signature)} text="Fetch Failed" />;
|
||||
} else if (!status.info) {
|
||||
return <RetryCard retry={refreshStatus} text="Not Found" />;
|
||||
return <RetryCard retry={() => refresh(signature)} text="Not Found" />;
|
||||
}
|
||||
|
||||
const { info } = status;
|
||||
|
@ -141,7 +128,10 @@ function StatusCard({ signature }: Props) {
|
|||
<div className="card">
|
||||
<div className="card-header align-items-center">
|
||||
<h3 className="card-header-title">Status</h3>
|
||||
<button className="btn btn-white btn-sm" onClick={refreshStatus}>
|
||||
<button
|
||||
className="btn btn-white btn-sm"
|
||||
onClick={() => refresh(signature)}
|
||||
>
|
||||
<span className="fe fe-refresh-cw mr-2"></span>
|
||||
Refresh
|
||||
</button>
|
||||
|
@ -179,14 +169,25 @@ function AccountsCard({ signature }: Props) {
|
|||
const dispatch = useDetailsDispatch();
|
||||
const { url } = useCluster();
|
||||
|
||||
const fetchStatus = useFetchTransactionStatus();
|
||||
const refreshStatus = () => fetchStatus(signature);
|
||||
const refreshDetails = () => fetchDetails(dispatch, signature, url);
|
||||
const transaction = details?.transaction?.transaction;
|
||||
const message = React.useMemo(() => {
|
||||
return transaction?.compileMessage();
|
||||
}, [transaction]);
|
||||
|
||||
if (!details) {
|
||||
const status = useTransactionStatus(signature);
|
||||
|
||||
if (!status || !status.info) {
|
||||
return null;
|
||||
} else if (!details) {
|
||||
return (
|
||||
<RetryCard
|
||||
retry={refreshStatus}
|
||||
text="Details are not available until the transaction reaches MAX confirmations"
|
||||
/>
|
||||
);
|
||||
} else if (details.fetchStatus === FetchStatus.Fetching) {
|
||||
return <LoadingCard />;
|
||||
} else if (details?.fetchStatus === FetchStatus.FetchFailed) {
|
||||
|
@ -571,11 +572,17 @@ function LoadingCard() {
|
|||
function RetryCard({ retry, text }: { retry: () => void; text: string }) {
|
||||
return (
|
||||
<div className="card">
|
||||
<div className="card-body">
|
||||
<div className="card-body text-center">
|
||||
{text}
|
||||
<span className="btn btn-white ml-3" onClick={retry}>
|
||||
<span className="btn btn-white ml-3 d-none d-md-inline" onClick={retry}>
|
||||
Try Again
|
||||
</span>
|
||||
<div className="d-block d-md-none mt-4">
|
||||
<hr></hr>
|
||||
<span className="btn btn-white" onClick={retry}>
|
||||
Try Again
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -2,24 +2,20 @@ import React from "react";
|
|||
import { Link } from "react-router-dom";
|
||||
import {
|
||||
useTransactions,
|
||||
useTransactionsDispatch,
|
||||
checkTransactionStatus,
|
||||
ActionType,
|
||||
TransactionStatus,
|
||||
Source,
|
||||
FetchStatus
|
||||
FetchStatus,
|
||||
useFetchTransactionStatus
|
||||
} from "../providers/transactions";
|
||||
import bs58 from "bs58";
|
||||
import { assertUnreachable } from "../utils";
|
||||
import { useCluster } from "../providers/cluster";
|
||||
import Copyable from "./Copyable";
|
||||
|
||||
function TransactionsCard() {
|
||||
const { transactions, idCounter } = useTransactions();
|
||||
const dispatch = useTransactionsDispatch();
|
||||
const fetchTransaction = useFetchTransactionStatus();
|
||||
const signatureInput = React.useRef<HTMLInputElement>(null);
|
||||
const [error, setError] = React.useState("");
|
||||
const { url } = useCluster();
|
||||
|
||||
const onNew = (signature: string) => {
|
||||
if (signature.length === 0) return;
|
||||
|
@ -37,13 +33,7 @@ function TransactionsCard() {
|
|||
return;
|
||||
}
|
||||
|
||||
dispatch({
|
||||
type: ActionType.FetchSignature,
|
||||
signature,
|
||||
source: Source.Input
|
||||
});
|
||||
checkTransactionStatus(dispatch, signature, url);
|
||||
|
||||
fetchTransaction(signature, Source.Input);
|
||||
const inputEl = signatureInput.current;
|
||||
if (inputEl) {
|
||||
inputEl.value = "";
|
||||
|
@ -98,9 +88,7 @@ function TransactionsCard() {
|
|||
<td>-</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
{transactions.map(transaction =>
|
||||
renderTransactionRow(transaction, dispatch, url)
|
||||
)}
|
||||
{transactions.map(transaction => renderTransactionRow(transaction))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
@ -120,11 +108,7 @@ const renderHeader = () => {
|
|||
);
|
||||
};
|
||||
|
||||
const renderTransactionRow = (
|
||||
transactionStatus: TransactionStatus,
|
||||
dispatch: any,
|
||||
url: string
|
||||
) => {
|
||||
const renderTransactionRow = (transactionStatus: TransactionStatus) => {
|
||||
const { fetchStatus, info, signature, id } = transactionStatus;
|
||||
|
||||
let statusText;
|
||||
|
@ -162,30 +146,6 @@ const renderTransactionRow = (
|
|||
confirmationsText = `${info.confirmations}`;
|
||||
}
|
||||
|
||||
const renderDetails = () => {
|
||||
if (info?.confirmations === "max") {
|
||||
return (
|
||||
<Link
|
||||
to={location => ({ ...location, pathname: "/tx/" + signature })}
|
||||
className="btn btn-rounded-circle btn-white btn-sm"
|
||||
>
|
||||
<span className="fe fe-arrow-right"></span>
|
||||
</Link>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<button
|
||||
className="btn btn-rounded-circle btn-white btn-sm"
|
||||
onClick={() => {
|
||||
checkTransactionStatus(dispatch, signature, url);
|
||||
}}
|
||||
>
|
||||
<span className="fe fe-refresh-cw"></span>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<tr key={signature}>
|
||||
<td>
|
||||
|
@ -201,7 +161,14 @@ const renderTransactionRow = (
|
|||
</td>
|
||||
<td className="text-uppercase">{confirmationsText}</td>
|
||||
<td>{slotText}</td>
|
||||
<td>{renderDetails()}</td>
|
||||
<td>
|
||||
<Link
|
||||
to={location => ({ ...location, pathname: "/tx/" + signature })}
|
||||
className="btn btn-rounded-circle btn-white btn-sm"
|
||||
>
|
||||
<span className="fe fe-arrow-right"></span>
|
||||
</Link>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -61,28 +61,24 @@ interface State {
|
|||
status: ClusterStatus;
|
||||
}
|
||||
|
||||
interface Connecting {
|
||||
status: ClusterStatus.Connecting;
|
||||
interface Action {
|
||||
status: ClusterStatus;
|
||||
cluster: Cluster;
|
||||
customUrl: string;
|
||||
}
|
||||
|
||||
interface Connected {
|
||||
status: ClusterStatus.Connected;
|
||||
}
|
||||
|
||||
interface Failure {
|
||||
status: ClusterStatus.Failure;
|
||||
}
|
||||
|
||||
type Action = Connected | Connecting | Failure;
|
||||
type Dispatch = (action: Action) => void;
|
||||
|
||||
function clusterReducer(state: State, action: Action): State {
|
||||
switch (action.status) {
|
||||
case ClusterStatus.Connected:
|
||||
case ClusterStatus.Failure: {
|
||||
return Object.assign({}, state, { status: action.status });
|
||||
if (
|
||||
state.cluster !== action.cluster ||
|
||||
state.customUrl !== action.customUrl
|
||||
)
|
||||
return state;
|
||||
return action;
|
||||
}
|
||||
case ClusterStatus.Connecting: {
|
||||
return action;
|
||||
|
@ -197,10 +193,10 @@ async function updateCluster(
|
|||
try {
|
||||
const connection = new Connection(clusterUrl(cluster, customUrl));
|
||||
await connection.getRecentBlockhash();
|
||||
dispatch({ status: ClusterStatus.Connected });
|
||||
dispatch({ status: ClusterStatus.Connected, cluster, customUrl });
|
||||
} catch (error) {
|
||||
console.error("Failed to update cluster", error);
|
||||
dispatch({ status: ClusterStatus.Failure });
|
||||
dispatch({ status: ClusterStatus.Failure, cluster, customUrl });
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ import {
|
|||
SignatureResult
|
||||
} from "@solana/web3.js";
|
||||
import { useQuery } from "../../utils/url";
|
||||
import { useCluster, Cluster } from "../cluster";
|
||||
import { useCluster, Cluster, ClusterStatus } from "../cluster";
|
||||
import {
|
||||
DetailsProvider,
|
||||
StateContext as DetailsStateContext,
|
||||
|
@ -127,7 +127,7 @@ export function TransactionsProvider({ children }: TransactionsProviderProps) {
|
|||
transactions: {}
|
||||
});
|
||||
|
||||
const { cluster, url } = useCluster();
|
||||
const { cluster, status: clusterStatus, url } = useCluster();
|
||||
const accountsDispatch = useAccountsDispatch();
|
||||
const query = useQuery();
|
||||
const testFlag = query.get("test");
|
||||
|
@ -140,14 +140,14 @@ export function TransactionsProvider({ children }: TransactionsProviderProps) {
|
|||
signature,
|
||||
source: Source.Url
|
||||
});
|
||||
checkTransactionStatus(dispatch, signature, url);
|
||||
fetchTransactionStatus(dispatch, signature, url, clusterStatus);
|
||||
});
|
||||
|
||||
// Create a test transaction
|
||||
if (cluster === Cluster.Devnet && testFlag !== null) {
|
||||
createTestTransaction(dispatch, accountsDispatch, url);
|
||||
createTestTransaction(dispatch, accountsDispatch, url, clusterStatus);
|
||||
}
|
||||
}, [testFlag, cluster, url]); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
}, [testFlag, cluster, clusterStatus, url]); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
|
||||
// Check for transactions in the url params
|
||||
const values = TX_ALIASES.flatMap(key => [
|
||||
|
@ -167,7 +167,7 @@ export function TransactionsProvider({ children }: TransactionsProviderProps) {
|
|||
signature,
|
||||
source: Source.Url
|
||||
});
|
||||
checkTransactionStatus(dispatch, signature, url);
|
||||
fetchTransactionStatus(dispatch, signature, url, clusterStatus);
|
||||
});
|
||||
}, [values.toString()]); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
|
||||
|
@ -183,7 +183,8 @@ export function TransactionsProvider({ children }: TransactionsProviderProps) {
|
|||
async function createTestTransaction(
|
||||
dispatch: Dispatch,
|
||||
accountsDispatch: AccountsDispatch,
|
||||
url: string
|
||||
url: string,
|
||||
clusterStatus: ClusterStatus
|
||||
) {
|
||||
const testKey = process.env.REACT_APP_TEST_KEY;
|
||||
let testAccount = new Account();
|
||||
|
@ -203,7 +204,7 @@ async function createTestTransaction(
|
|||
signature,
|
||||
source: Source.Test
|
||||
});
|
||||
checkTransactionStatus(dispatch, signature, url);
|
||||
fetchTransactionStatus(dispatch, signature, url, clusterStatus);
|
||||
accountsDispatch({
|
||||
type: AccountsActionType.Input,
|
||||
pubkey: testAccount.publicKey
|
||||
|
@ -226,16 +227,17 @@ async function createTestTransaction(
|
|||
signature,
|
||||
source: Source.Test
|
||||
});
|
||||
checkTransactionStatus(dispatch, signature, url);
|
||||
fetchTransactionStatus(dispatch, signature, url, clusterStatus);
|
||||
} catch (error) {
|
||||
console.error("Failed to create test failure transaction", error);
|
||||
}
|
||||
}
|
||||
|
||||
export async function checkTransactionStatus(
|
||||
export async function fetchTransactionStatus(
|
||||
dispatch: Dispatch,
|
||||
signature: TransactionSignature,
|
||||
url: string
|
||||
url: string,
|
||||
status: ClusterStatus
|
||||
) {
|
||||
dispatch({
|
||||
type: ActionType.UpdateStatus,
|
||||
|
@ -243,6 +245,9 @@ export async function checkTransactionStatus(
|
|||
fetchStatus: FetchStatus.Fetching
|
||||
});
|
||||
|
||||
// We will auto-refetch when status is no longer connecting
|
||||
if (status === ClusterStatus.Connecting) return;
|
||||
|
||||
let fetchStatus;
|
||||
let info: TransactionStatusInfo | undefined;
|
||||
try {
|
||||
|
@ -317,16 +322,6 @@ export function useTransactionDetails(signature: TransactionSignature) {
|
|||
return context[signature];
|
||||
}
|
||||
|
||||
export function useTransactionsDispatch() {
|
||||
const context = React.useContext(DispatchContext);
|
||||
if (!context) {
|
||||
throw new Error(
|
||||
`useTransactionsDispatch must be used within a TransactionsProvider`
|
||||
);
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
export function useDetailsDispatch() {
|
||||
const context = React.useContext(DetailsDispatchContext);
|
||||
if (!context) {
|
||||
|
@ -336,3 +331,25 @@ export function useDetailsDispatch() {
|
|||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
export function useFetchTransactionStatus() {
|
||||
const dispatch = React.useContext(DispatchContext);
|
||||
if (!dispatch) {
|
||||
throw new Error(
|
||||
`useFetchTransactionStatus must be used within a TransactionsProvider`
|
||||
);
|
||||
}
|
||||
|
||||
const { url, status } = useCluster();
|
||||
return (signature: TransactionSignature, source?: Source) => {
|
||||
if (source !== undefined) {
|
||||
dispatch({
|
||||
type: ActionType.FetchSignature,
|
||||
signature,
|
||||
source
|
||||
});
|
||||
}
|
||||
|
||||
fetchTransactionStatus(dispatch, signature, url, status);
|
||||
};
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue