diff --git a/explorer/src/pages/TransactionDetailsPage.tsx b/explorer/src/pages/TransactionDetailsPage.tsx
index b024313ac6..eb3696a78d 100644
--- a/explorer/src/pages/TransactionDetailsPage.tsx
+++ b/explorer/src/pages/TransactionDetailsPage.tsx
@@ -49,31 +49,41 @@ export function TransactionDetailsPage({ signature }: Props) {
function StatusCard({ signature }: Props) {
const fetchStatus = useFetchTransactionStatus();
const status = useTransactionStatus(signature);
- const refresh = useFetchTransactionStatus();
+ const fetchDetails = useFetchTransactionDetails();
const details = useTransactionDetails(signature);
const { firstAvailableBlock, status: clusterStatus } = useCluster();
+ const refresh = React.useCallback(
+ (signature: string) => {
+ fetchStatus(signature);
+ fetchDetails(signature);
+ },
+ [fetchStatus, fetchDetails]
+ );
// Fetch transaction on load
React.useEffect(() => {
- if (!status && clusterStatus === ClusterStatus.Connected)
+ if (!status && clusterStatus === ClusterStatus.Connected) {
fetchStatus(signature);
+ }
}, [signature, clusterStatus]); // eslint-disable-line react-hooks/exhaustive-deps
if (!status || status.fetchStatus === FetchStatus.Fetching) {
return ;
} else if (status?.fetchStatus === FetchStatus.FetchFailed) {
- return refresh(signature)} text="Fetch Failed" />;
+ return (
+ fetchStatus(signature)} text="Fetch Failed" />
+ );
} else if (!status.info) {
if (firstAvailableBlock !== undefined) {
return (
refresh(signature)}
+ retry={() => fetchStatus(signature)}
text="Not Found"
subtext={`Note: Transactions processed before block ${firstAvailableBlock} are not available at this time`}
/>
);
}
- return refresh(signature)} text="Not Found" />;
+ return fetchStatus(signature)} text="Not Found" />;
}
const { info } = status;
@@ -187,9 +197,8 @@ function StatusCard({ signature }: Props) {
}
function AccountsCard({ signature }: Props) {
- const details = useTransactionDetails(signature);
-
const { url } = useCluster();
+ const details = useTransactionDetails(signature);
const fetchStatus = useFetchTransactionStatus();
const fetchDetails = useFetchTransactionDetails();
const refreshStatus = () => fetchStatus(signature);
@@ -198,6 +207,13 @@ function AccountsCard({ signature }: Props) {
const message = transaction?.message;
const status = useTransactionStatus(signature);
+ // Fetch details on load
+ React.useEffect(() => {
+ if (status?.info?.confirmations === "max" && !details) {
+ fetchDetails(signature);
+ }
+ }, [signature, details, status, fetchDetails]);
+
if (!status || !status.info) {
return null;
} else if (!details) {
diff --git a/explorer/src/providers/accounts/index.tsx b/explorer/src/providers/accounts/index.tsx
index f34f96c561..45a35f0f41 100644
--- a/explorer/src/providers/accounts/index.tsx
+++ b/explorer/src/providers/accounts/index.tsx
@@ -29,6 +29,7 @@ export interface Account {
type Accounts = { [address: string]: Account };
interface State {
accounts: Accounts;
+ url: string;
}
export enum ActionType {
@@ -39,6 +40,7 @@ export enum ActionType {
interface Update {
type: ActionType.Update;
+ url: string;
pubkey: PublicKey;
data: {
status: FetchStatus;
@@ -49,17 +51,25 @@ interface Update {
interface Fetch {
type: ActionType.Fetch;
+ url: string;
pubkey: PublicKey;
}
interface Clear {
type: ActionType.Clear;
+ url: string;
}
type Action = Update | Fetch | Clear;
type Dispatch = (action: Action) => void;
function reducer(state: State, action: Action): State {
+ if (action.type === ActionType.Clear) {
+ return { url: action.url, accounts: {} };
+ } else if (action.url !== state.url) {
+ return state;
+ }
+
switch (action.type) {
case ActionType.Fetch: {
const address = action.pubkey.toBase58();
@@ -100,13 +110,6 @@ function reducer(state: State, action: Action): State {
}
break;
}
-
- case ActionType.Clear: {
- return {
- ...state,
- accounts: {},
- };
- }
}
return state;
}
@@ -116,14 +119,15 @@ const DispatchContext = React.createContext(undefined);
type AccountsProviderProps = { children: React.ReactNode };
export function AccountsProvider({ children }: AccountsProviderProps) {
+ const { url } = useCluster();
const [state, dispatch] = React.useReducer(reducer, {
+ url,
accounts: {},
});
// Clear account statuses whenever cluster is changed
- const { url } = useCluster();
React.useEffect(() => {
- dispatch({ type: ActionType.Clear });
+ dispatch({ type: ActionType.Clear, url });
}, [url]);
return (
@@ -145,6 +149,7 @@ async function fetchAccountInfo(
dispatch({
type: ActionType.Fetch,
pubkey,
+ url,
});
let fetchStatus;
@@ -182,7 +187,7 @@ async function fetchAccountInfo(
fetchStatus = FetchStatus.FetchFailed;
}
const data = { status: fetchStatus, lamports, details };
- dispatch({ type: ActionType.Update, data, pubkey });
+ dispatch({ type: ActionType.Update, data, pubkey, url });
}
export function useAccounts() {
diff --git a/explorer/src/providers/transactions/details.tsx b/explorer/src/providers/transactions/details.tsx
index 0427d3fa77..d20775bd15 100644
--- a/explorer/src/providers/transactions/details.tsx
+++ b/explorer/src/providers/transactions/details.tsx
@@ -5,7 +5,7 @@ import {
ParsedConfirmedTransaction,
} from "@solana/web3.js";
import { useCluster } from "../cluster";
-import { useTransactions, FetchStatus } from "./index";
+import { FetchStatus } from "./index";
import { CACHED_DETAILS, isCached } from "./cached";
export interface Details {
@@ -13,68 +13,69 @@ export interface Details {
transaction: ParsedConfirmedTransaction | null;
}
-type State = { [signature: string]: Details };
+type State = {
+ entries: { [signature: string]: Details };
+ url: string;
+};
export enum ActionType {
Update,
- Add,
Clear,
}
interface Update {
type: ActionType.Update;
+ url: string;
signature: string;
fetchStatus: FetchStatus;
transaction: ParsedConfirmedTransaction | null;
}
-interface Add {
- type: ActionType.Add;
- signature: TransactionSignature;
-}
-
interface Clear {
type: ActionType.Clear;
+ url: string;
}
-type Action = Update | Add | Clear;
+type Action = Update | Clear;
type Dispatch = (action: Action) => void;
function reducer(state: State, action: Action): State {
- switch (action.type) {
- case ActionType.Add: {
- const details = { ...state };
- const signature = action.signature;
- if (!details[signature]) {
- details[signature] = {
- fetchStatus: FetchStatus.Fetching,
- transaction: null,
- };
- }
- return details;
- }
+ if (action.type === ActionType.Clear) {
+ return { url: action.url, entries: {} };
+ } else if (action.url !== state.url) {
+ return state;
+ }
+ switch (action.type) {
case ActionType.Update: {
- let details = state[action.signature];
+ const signature = action.signature;
+ const details = state.entries[signature];
if (details) {
- details = {
- ...details,
- fetchStatus: action.fetchStatus,
- transaction: action.transaction,
- };
return {
...state,
- [action.signature]: details,
+ entries: {
+ ...state.entries,
+ [signature]: {
+ ...details,
+ fetchStatus: action.fetchStatus,
+ transaction: action.transaction,
+ },
+ },
+ };
+ } else {
+ return {
+ ...state,
+ entries: {
+ ...state.entries,
+ [signature]: {
+ fetchStatus: FetchStatus.Fetching,
+ transaction: null,
+ },
+ },
};
}
- break;
- }
-
- case ActionType.Clear: {
- return {};
}
}
- return state;
}
export const StateContext = React.createContext(undefined);
@@ -84,28 +85,13 @@ export const DispatchContext = React.createContext(
type DetailsProviderProps = { children: React.ReactNode };
export function DetailsProvider({ children }: DetailsProviderProps) {
- const [state, dispatch] = React.useReducer(reducer, {});
- const { transactions, lastFetched } = useTransactions();
const { url } = useCluster();
+ const [state, dispatch] = React.useReducer(reducer, { url, entries: {} });
React.useEffect(() => {
- dispatch({ type: ActionType.Clear });
+ dispatch({ type: ActionType.Clear, url });
}, [url]);
- // Filter blocks for current transaction slots
- React.useEffect(() => {
- if (lastFetched) {
- const confirmed =
- transactions[lastFetched] &&
- transactions[lastFetched].info?.confirmations === "max";
- const noDetails = !state[lastFetched];
- if (confirmed && noDetails) {
- dispatch({ type: ActionType.Add, signature: lastFetched });
- fetchDetails(dispatch, lastFetched, url);
- }
- }
- }, [transactions, lastFetched]); // eslint-disable-line react-hooks/exhaustive-deps
-
return (
@@ -125,6 +111,7 @@ async function fetchDetails(
fetchStatus: FetchStatus.Fetching,
transaction: null,
signature,
+ url,
});
let fetchStatus;
@@ -143,7 +130,13 @@ async function fetchDetails(
fetchStatus = FetchStatus.FetchFailed;
}
}
- dispatch({ type: ActionType.Update, fetchStatus, signature, transaction });
+ dispatch({
+ type: ActionType.Update,
+ fetchStatus,
+ signature,
+ transaction,
+ url,
+ });
}
export function useFetchTransactionDetails() {
diff --git a/explorer/src/providers/transactions/index.tsx b/explorer/src/providers/transactions/index.tsx
index 77aa77cae4..6834824b94 100644
--- a/explorer/src/providers/transactions/index.tsx
+++ b/explorer/src/providers/transactions/index.tsx
@@ -44,7 +44,7 @@ export interface TransactionStatus {
type Transactions = { [signature: string]: TransactionStatus };
interface State {
transactions: Transactions;
- lastFetched: TransactionSignature | undefined;
+ url: string;
}
export enum ActionType {
@@ -55,6 +55,7 @@ export enum ActionType {
interface UpdateStatus {
type: ActionType.UpdateStatus;
+ url: string;
signature: TransactionSignature;
fetchStatus: FetchStatus;
info?: TransactionStatusInfo;
@@ -62,17 +63,25 @@ interface UpdateStatus {
interface FetchSignature {
type: ActionType.FetchSignature;
+ url: string;
signature: TransactionSignature;
}
interface Clear {
type: ActionType.Clear;
+ url: string;
}
type Action = UpdateStatus | FetchSignature | Clear;
type Dispatch = (action: Action) => void;
function reducer(state: State, action: Action): State {
+ if (action.type === ActionType.Clear) {
+ return { url: action.url, transactions: {} };
+ } else if (action.url !== state.url) {
+ return state;
+ }
+
switch (action.type) {
case ActionType.FetchSignature: {
const signature = action.signature;
@@ -86,7 +95,7 @@ function reducer(state: State, action: Action): State {
info: undefined,
},
};
- return { ...state, transactions, lastFetched: signature };
+ return { ...state, transactions };
} else {
const transactions = {
...state.transactions,
@@ -95,7 +104,7 @@ function reducer(state: State, action: Action): State {
fetchStatus: FetchStatus.Fetching,
},
};
- return { ...state, transactions, lastFetched: signature };
+ return { ...state, transactions };
}
}
@@ -114,13 +123,6 @@ function reducer(state: State, action: Action): State {
}
break;
}
-
- case ActionType.Clear: {
- return {
- ...state,
- transactions: {},
- };
- }
}
return state;
}
@@ -132,12 +134,12 @@ const DispatchContext = React.createContext(undefined);
type TransactionsProviderProps = { children: React.ReactNode };
export function TransactionsProvider({ children }: TransactionsProviderProps) {
+ const { cluster, status: clusterStatus, url } = useCluster();
const [state, dispatch] = React.useReducer(reducer, {
transactions: {},
- lastFetched: undefined,
+ url,
});
- const { cluster, status: clusterStatus, url } = useCluster();
const fetchAccount = useFetchAccountInfo();
const query = useQuery();
const testFlag = query.get("test");
@@ -145,9 +147,7 @@ export function TransactionsProvider({ children }: TransactionsProviderProps) {
// Check transaction statuses whenever cluster updates
React.useEffect(() => {
if (clusterStatus === ClusterStatus.Connecting) {
- dispatch({ type: ActionType.Clear });
- } else if (clusterStatus === ClusterStatus.Connected && state.lastFetched) {
- fetchTransactionStatus(dispatch, state.lastFetched, url);
+ dispatch({ type: ActionType.Clear, url });
}
// Create a test transaction
@@ -216,6 +216,7 @@ export async function fetchTransactionStatus(
dispatch({
type: ActionType.FetchSignature,
signature,
+ url,
});
let fetchStatus;
@@ -261,6 +262,7 @@ export async function fetchTransactionStatus(
signature,
fetchStatus,
info,
+ url,
});
}
@@ -295,7 +297,7 @@ export function useTransactionDetails(signature: TransactionSignature) {
);
}
- return context[signature];
+ return context.entries[signature];
}
export function useFetchTransactionStatus() {