Refactor transaction provider

This commit is contained in:
Justin Starry 2020-04-29 11:00:06 +08:00 committed by Michael Vines
parent f3e677eaab
commit 6eff7f35c9
4 changed files with 93 additions and 96 deletions

View File

@ -3,15 +3,15 @@ import {
useDetails, useDetails,
useTransactions, useTransactions,
useTransactionsDispatch, useTransactionsDispatch,
ActionType, ActionType
Selected
} from "../providers/transactions"; } from "../providers/transactions";
import { displayAddress, decodeCreate, decodeTransfer } from "../utils/tx"; import { displayAddress, decodeCreate, decodeTransfer } from "../utils/tx";
import { import {
LAMPORTS_PER_SOL, LAMPORTS_PER_SOL,
TransferParams, TransferParams,
CreateAccountParams, CreateAccountParams,
TransactionInstruction TransactionInstruction,
TransactionSignature
} from "@solana/web3.js"; } from "@solana/web3.js";
import Copyable from "./Copyable"; import Copyable from "./Copyable";
import Overlay from "./Overlay"; import Overlay from "./Overlay";
@ -36,7 +36,7 @@ function TransactionModal() {
</button> </button>
</div> </div>
<TransactionDetails selected={selected} /> <TransactionDetails signature={selected} />
</div> </div>
</div> </div>
</div> </div>
@ -53,8 +53,12 @@ function TransactionModal() {
); );
} }
function TransactionDetails({ selected }: { selected: Selected }) { function TransactionDetails({
const details = useDetails(selected.signature); signature
}: {
signature: TransactionSignature;
}) {
const details = useDetails(signature);
const renderError = (content: React.ReactNode) => { const renderError = (content: React.ReactNode) => {
return ( return (

View File

@ -4,8 +4,8 @@ import {
useTransactionsDispatch, useTransactionsDispatch,
checkTransactionStatus, checkTransactionStatus,
ActionType, ActionType,
Transaction, TransactionState,
Status FetchStatus
} from "../providers/transactions"; } from "../providers/transactions";
import bs58 from "bs58"; import bs58 from "bs58";
import { assertUnreachable } from "../utils"; import { assertUnreachable } from "../utils";
@ -115,50 +115,50 @@ const renderHeader = () => {
}; };
const renderTransactionRow = ( const renderTransactionRow = (
transaction: Transaction, transaction: TransactionState,
dispatch: any, dispatch: any,
url: string url: string
) => { ) => {
const { fetchStatus, transactionStatus } = transaction;
let statusText; let statusText;
let statusClass; let statusClass;
switch (transaction.status) { switch (fetchStatus) {
case Status.CheckFailed: case FetchStatus.FetchFailed:
statusClass = "dark"; statusClass = "dark";
statusText = "Cluster Error"; statusText = "Cluster Error";
break; break;
case Status.Checking: case FetchStatus.Fetching:
statusClass = "info"; statusClass = "info";
statusText = "Checking"; statusText = "Fetching";
break; break;
case Status.Success: case FetchStatus.Fetched: {
statusClass = "success"; if (!transactionStatus) {
statusText = "Success"; statusClass = "warning";
break; statusText = "Not Found";
case Status.Failure: } else if (transactionStatus.result.err) {
statusClass = "danger"; statusClass = "danger";
statusText = "Failed"; statusText = "Failed";
break; } else {
case Status.Missing: statusClass = "success";
statusClass = "warning"; statusText = "Success";
statusText = "Not Found"; }
break; break;
}
default: default:
return assertUnreachable(transaction.status); return assertUnreachable(fetchStatus);
} }
let slotText = "-"; let slotText = "-";
if (transaction.slot !== undefined) {
slotText = `${transaction.slot}`;
}
let confirmationsText = "-"; let confirmationsText = "-";
if (transaction.confirmations !== undefined) { if (transactionStatus) {
confirmationsText = `${transaction.confirmations}`; slotText = `${transactionStatus.slot}`;
confirmationsText = `${transactionStatus.confirmations}`;
} }
const renderDetails = () => { const renderDetails = () => {
let onClick, icon; let onClick, icon;
if (transaction.confirmations === "max") { if (transactionStatus?.confirmations === "max") {
icon = "more-horizontal"; icon = "more-horizontal";
onClick = () => onClick = () =>
dispatch({ dispatch({

View File

@ -97,9 +97,9 @@ export function DetailsProvider({ children }: DetailsProviderProps) {
if (status !== ClusterStatus.Connected) return; if (status !== ClusterStatus.Connected) return;
const fetchSignatures = new Set<string>(); const fetchSignatures = new Set<string>();
transactions.forEach(tx => { transactions.forEach(({ signature, transactionStatus }) => {
if (tx.slot && tx.confirmations === "max" && !state[tx.signature]) if (transactionStatus?.confirmations === "max" && !state[signature])
fetchSignatures.add(tx.signature); fetchSignatures.add(signature);
}); });
const fetchList: string[] = []; const fetchList: string[] = [];

View File

@ -3,7 +3,8 @@ import {
TransactionSignature, TransactionSignature,
Connection, Connection,
SystemProgram, SystemProgram,
Account Account,
SignatureResult
} from "@solana/web3.js"; } from "@solana/web3.js";
import { findGetParameter, findPathSegment } from "../../utils/url"; import { findGetParameter, findPathSegment } from "../../utils/url";
import { useCluster, ClusterStatus } from "../cluster"; import { useCluster, ClusterStatus } from "../cluster";
@ -20,12 +21,10 @@ import {
ActionType as AccountsActionType ActionType as AccountsActionType
} from "../accounts"; } from "../accounts";
export enum Status { export enum FetchStatus {
Checking, Fetching,
CheckFailed, FetchFailed,
Success, Fetched
Failure,
Missing
} }
enum Source { enum Source {
@ -35,24 +34,24 @@ enum Source {
export type Confirmations = number | "max"; export type Confirmations = number | "max";
export interface Transaction { export interface TransactionStatus {
id: number;
status: Status;
source: Source;
slot?: number;
confirmations?: Confirmations;
signature: TransactionSignature;
}
export interface Selected {
slot: number; slot: number;
signature: TransactionSignature; result: SignatureResult;
confirmations: Confirmations;
} }
type Transactions = { [signature: string]: Transaction }; export interface TransactionState {
id: number;
source: Source;
fetchStatus: FetchStatus;
signature: TransactionSignature;
transactionStatus?: TransactionStatus;
}
type Transactions = { [signature: string]: TransactionState };
interface State { interface State {
idCounter: number; idCounter: number;
selected?: Selected; selected?: TransactionSignature;
transactions: Transactions; transactions: Transactions;
} }
@ -75,9 +74,8 @@ interface DeselectTransaction {
interface UpdateStatus { interface UpdateStatus {
type: ActionType.UpdateStatus; type: ActionType.UpdateStatus;
signature: TransactionSignature; signature: TransactionSignature;
status: Status; fetchStatus: FetchStatus;
slot?: number; transactionStatus?: TransactionStatus;
confirmations?: Confirmations;
} }
interface InputSignature { interface InputSignature {
@ -90,6 +88,7 @@ type Action =
| InputSignature | InputSignature
| SelectTransaction | SelectTransaction
| DeselectTransaction; | DeselectTransaction;
type Dispatch = (action: Action) => void; type Dispatch = (action: Action) => void;
function reducer(state: State, action: Action): State { function reducer(state: State, action: Action): State {
@ -99,37 +98,33 @@ function reducer(state: State, action: Action): State {
} }
case ActionType.Select: { case ActionType.Select: {
const tx = state.transactions[action.signature]; const tx = state.transactions[action.signature];
if (!tx.slot) return state; return { ...state, selected: tx.signature };
const selected = {
slot: tx.slot,
signature: tx.signature
};
return { ...state, selected };
} }
case ActionType.InputSignature: { case ActionType.InputSignature: {
if (!!state.transactions[action.signature]) return state; if (!!state.transactions[action.signature]) return state;
const idCounter = state.idCounter + 1; const nextId = state.idCounter + 1;
const transactions = { const transactions = {
...state.transactions, ...state.transactions,
[action.signature]: { [action.signature]: {
id: idCounter, id: nextId,
status: Status.Checking,
source: Source.Input, source: Source.Input,
signature: action.signature signature: action.signature,
fetchStatus: FetchStatus.Fetching
} }
}; };
return { ...state, transactions, idCounter }; return { ...state, transactions, idCounter: nextId };
} }
case ActionType.UpdateStatus: { case ActionType.UpdateStatus: {
let transaction = state.transactions[action.signature]; let transaction = state.transactions[action.signature];
if (transaction) { if (transaction) {
transaction = { transaction = {
...transaction, ...transaction,
status: action.status, fetchStatus: action.fetchStatus
slot: action.slot,
confirmations: action.confirmations
}; };
if (action.transactionStatus) {
transaction.transactionStatus = action.transactionStatus;
}
const transactions = { const transactions = {
...state.transactions, ...state.transactions,
[action.signature]: transaction [action.signature]: transaction
@ -171,13 +166,14 @@ function initState(): State {
const transactions = signatures.reduce( const transactions = signatures.reduce(
(transactions: Transactions, signature) => { (transactions: Transactions, signature) => {
if (!!transactions[signature]) return transactions; if (!!transactions[signature]) return transactions;
idCounter++; const nextId = idCounter + 1;
transactions[signature] = { transactions[signature] = {
id: idCounter, id: nextId,
source: Source.Url,
signature, signature,
status: Status.Checking, fetchStatus: FetchStatus.Fetching
source: Source.Url
}; };
idCounter++;
return transactions; return transactions;
}, },
{} {}
@ -269,44 +265,41 @@ export async function checkTransactionStatus(
) { ) {
dispatch({ dispatch({
type: ActionType.UpdateStatus, type: ActionType.UpdateStatus,
status: Status.Checking, signature,
signature fetchStatus: FetchStatus.Fetching
}); });
let status; let fetchStatus;
let slot; let transactionStatus: TransactionStatus | undefined;
let confirmations: Confirmations | undefined;
try { try {
const { value } = await new Connection(url).getSignatureStatus(signature, { const { value } = await new Connection(url).getSignatureStatus(signature, {
searchTransactionHistory: true searchTransactionHistory: true
}); });
if (value === null) { if (value !== null) {
status = Status.Missing; let confirmations: Confirmations;
} else {
slot = value.slot;
if (typeof value.confirmations === "number") { if (typeof value.confirmations === "number") {
confirmations = value.confirmations; confirmations = value.confirmations;
} else { } else {
confirmations = "max"; confirmations = "max";
} }
if (value.err) { transactionStatus = {
status = Status.Failure; slot: value.slot,
} else { confirmations,
status = Status.Success; result: { err: value.err }
} };
} }
fetchStatus = FetchStatus.Fetched;
} catch (error) { } catch (error) {
console.error("Failed to check transaction status", error); console.error("Failed to fetch transaction status", error);
status = Status.CheckFailed; fetchStatus = FetchStatus.FetchFailed;
} }
dispatch({ dispatch({
type: ActionType.UpdateStatus, type: ActionType.UpdateStatus,
status, signature,
slot, fetchStatus,
confirmations, transactionStatus
signature
}); });
} }