-
-
Beta
- Solana Explorer
-
-
-
+
+
+
+
+
+
+
+
Beta
+ Solana Explorer
+
+
-
+
+
+
-
+
);
}
diff --git a/src/components/networkStatusButton.tsx b/src/components/NetworkStatusButton.tsx
similarity index 72%
rename from src/components/networkStatusButton.tsx
rename to src/components/NetworkStatusButton.tsx
index 3181b1ba0eb6fe..34869ad391c942 100644
--- a/src/components/networkStatusButton.tsx
+++ b/src/components/NetworkStatusButton.tsx
@@ -6,22 +6,22 @@ function NetworkStatusButton() {
switch (status) {
case NetworkStatus.Connected:
- return
{url};
+ return
{url};
case NetworkStatus.Connecting:
return (
-
+
{"Connecting "}
-
+
);
case NetworkStatus.Failure:
- return
Disconnected;
+ return
Disconnected;
}
}
diff --git a/src/components/TransactionsCard.tsx b/src/components/TransactionsCard.tsx
new file mode 100644
index 00000000000000..7223d653f1f6b4
--- /dev/null
+++ b/src/components/TransactionsCard.tsx
@@ -0,0 +1,88 @@
+import React from "react";
+import {
+ useTransactions,
+ Transaction,
+ Status
+} from "../providers/transactions";
+
+function TransactionsCard() {
+ const { transactions } = useTransactions();
+
+ return (
+
+ {renderHeader()}
+
+
+
+
+
+ Status |
+ Signature |
+ Confirmations |
+ Slot Number |
+
+
+
+ {Object.values(transactions).map(transaction =>
+ renderTransactionRow(transaction)
+ )}
+
+
+
+
+ );
+}
+
+const renderHeader = () => {
+ return (
+
+ );
+};
+
+const renderTransactionRow = (transaction: Transaction) => {
+ let statusText;
+ let statusClass;
+ switch (transaction.status) {
+ case Status.CheckFailed:
+ statusClass = "dark";
+ statusText = "Network Error";
+ break;
+ case Status.Checking:
+ statusClass = "info";
+ statusText = "Checking";
+ break;
+ case Status.Success:
+ statusClass = "success";
+ statusText = "Success";
+ break;
+ case Status.Failure:
+ statusClass = "danger";
+ statusText = "Failed";
+ break;
+ case Status.Pending:
+ statusClass = "warning";
+ statusText = "Pending";
+ break;
+ }
+
+ return (
+
+
+ {statusText}
+ |
+
+ {transaction.signature}
+ |
+ TODO |
+ TODO |
+
+ );
+};
+
+export default TransactionsCard;
diff --git a/src/providers/transactions.tsx b/src/providers/transactions.tsx
new file mode 100644
index 00000000000000..4aca742259128a
--- /dev/null
+++ b/src/providers/transactions.tsx
@@ -0,0 +1,139 @@
+import React from "react";
+import { TransactionSignature, Connection } from "@solana/web3.js";
+import { findGetParameter } from "../utils";
+import { useNetwork } from "../providers/network";
+
+export enum Status {
+ Checking,
+ CheckFailed,
+ Success,
+ Failure,
+ Pending
+}
+
+export interface Transaction {
+ id: number;
+ status: Status;
+ recent: boolean;
+ signature: TransactionSignature;
+}
+
+type Transactions = { [id: number]: Transaction };
+interface State {
+ idCounter: number;
+ transactions: Transactions;
+}
+
+interface UpdateStatus {
+ id: number;
+ status: Status;
+}
+
+type Action = UpdateStatus;
+type Dispatch = (action: Action) => void;
+
+function reducer(state: State, action: Action): State {
+ let transaction = state.transactions[action.id];
+ if (transaction) {
+ transaction = { ...transaction, status: action.status };
+ const transactions = { ...state.transactions, [action.id]: transaction };
+ return { ...state, transactions };
+ }
+ return state;
+}
+
+function initState(): State {
+ let idCounter = 0;
+ const signatures = findGetParameter("txs")?.split(",") || [];
+ const transactions = signatures.reduce(
+ (transactions: Transactions, signature) => {
+ const id = ++idCounter;
+ transactions[id] = {
+ id,
+ status: Status.Checking,
+ recent: true,
+ signature
+ };
+ return transactions;
+ },
+ {}
+ );
+ return { idCounter, transactions };
+}
+
+const StateContext = React.createContext
(undefined);
+const DispatchContext = React.createContext(undefined);
+
+type TransactionsProviderProps = { children: React.ReactNode };
+export function TransactionsProvider({ children }: TransactionsProviderProps) {
+ const [state, dispatch] = React.useReducer(reducer, undefined, initState);
+
+ const { status, url } = useNetwork();
+
+ // Check transaction statuses on startup and whenever network updates
+ React.useEffect(() => {
+ const connection = new Connection(url);
+ Object.values(state.transactions).forEach(tx => {
+ checkTransactionStatus(dispatch, tx, connection);
+ });
+ }, [status, url]); // eslint-disable-line react-hooks/exhaustive-deps
+
+ return (
+
+
+ {children}
+
+
+ );
+}
+
+export async function checkTransactionStatus(
+ dispatch: Dispatch,
+ transaction: Transaction,
+ connection: Connection
+) {
+ const id = transaction.id;
+ dispatch({
+ status: Status.Checking,
+ id
+ });
+
+ let status;
+ try {
+ const signatureStatus = await connection.getSignatureStatus(
+ transaction.signature
+ );
+
+ if (signatureStatus === null) {
+ status = Status.Pending;
+ } else if ("Ok" in signatureStatus) {
+ status = Status.Success;
+ } else {
+ status = Status.Failure;
+ }
+ } catch (error) {
+ console.error("Failed to check transaction status", error);
+ status = Status.CheckFailed;
+ }
+ dispatch({ status, id });
+}
+
+export function useTransactions() {
+ const context = React.useContext(StateContext);
+ if (!context) {
+ throw new Error(
+ `useTransactions must be used within a TransactionsProvider`
+ );
+ }
+ return context;
+}
+
+export function useTransactionsDispatch() {
+ const context = React.useContext(DispatchContext);
+ if (!context) {
+ throw new Error(
+ `useTransactionsDispatch must be used within a TransactionsProvider`
+ );
+ }
+ return context;
+}
diff --git a/src/scss/_solana.scss b/src/scss/_solana.scss
index 740082f2ebc2b5..20ff90948ffa00 100644
--- a/src/scss/_solana.scss
+++ b/src/scss/_solana.scss
@@ -2,3 +2,10 @@
// solana.scss
// Use this to write your custom SCSS
//
+
+code {
+ padding: 0.33rem;
+ border-radius: $border-radius;
+ background-color: $gray-200;
+ color: $black;
+}