solana/explorer/src/components/AccountsCard.tsx

191 lines
5.1 KiB
TypeScript

import React from "react";
import { Link } from "react-router-dom";
import {
useAccounts,
Account,
Status,
useFetchAccountInfo
} from "../providers/accounts";
import { assertUnreachable } from "../utils";
import { displayAddress } from "../utils/tx";
import { PublicKey } from "@solana/web3.js";
import Copyable from "./Copyable";
import { lamportsToSolString } from "utils";
function AccountsCard() {
const { accounts, idCounter } = useAccounts();
const fetchAccountInfo = useFetchAccountInfo();
const addressInput = React.useRef<HTMLInputElement>(null);
const [error, setError] = React.useState("");
const onNew = (address: string) => {
if (address.length === 0) return;
let pubkey;
try {
pubkey = new PublicKey(address);
} catch (err) {
setError(`${err}`);
return;
}
fetchAccountInfo(pubkey);
const inputEl = addressInput.current;
if (inputEl) {
inputEl.value = "";
}
};
return (
<div className="card">
{renderHeader()}
<div className="table-responsive mb-0">
<table className="table table-sm table-nowrap card-table">
<thead>
<tr>
<th className="text-muted">
<span className="fe fe-hash"></span>
</th>
<th className="text-muted">Status</th>
<th className="text-muted">Address</th>
<th className="text-muted">Balance (SOL)</th>
<th className="text-muted">Data (bytes)</th>
<th className="text-muted">Owner</th>
<th className="text-muted">Details</th>
</tr>
</thead>
<tbody className="list">
<tr>
<td>
<span className="badge badge-soft-dark badge-pill">
{idCounter + 1}
</span>
</td>
<td>
<span className={`badge badge-soft-dark`}>New</span>
</td>
<td>
<input
type="text"
onInput={() => setError("")}
onKeyDown={e =>
e.keyCode === 13 && onNew(e.currentTarget.value)
}
onSubmit={e => onNew(e.currentTarget.value)}
ref={addressInput}
className={`form-control text-address text-monospace ${
error ? "is-invalid" : ""
}`}
placeholder="input account address"
/>
{error ? <div className="invalid-feedback">{error}</div> : null}
</td>
<td>-</td>
<td>-</td>
<td>-</td>
<td></td>
</tr>
{accounts.map(account => renderAccountRow(account))}
</tbody>
</table>
</div>
</div>
);
}
const renderHeader = () => {
return (
<div className="card-header">
<div className="row align-items-center">
<div className="col">
<h4 className="card-header-title">Look Up Account(s)</h4>
</div>
</div>
</div>
);
};
const renderAccountRow = (account: Account) => {
let statusText;
let statusClass;
switch (account.status) {
case Status.CheckFailed:
case Status.HistoryFailed:
statusClass = "dark";
statusText = "Cluster Error";
break;
case Status.Checking:
case Status.FetchingHistory:
statusClass = "info";
statusText = "Fetching";
break;
case Status.Success:
if (account.details?.executable) {
statusClass = "dark";
statusText = "Executable";
} else if (account.lamports) {
statusClass = "success";
statusText = "Found";
} else {
statusClass = "danger";
statusText = "Not Found";
}
break;
default:
return assertUnreachable(account.status);
}
let data = "-";
let owner = "-";
if (account.details) {
data = `${account.details.space}`;
owner = displayAddress(account.details.owner.toBase58());
}
let balance = "-";
if (account.lamports !== undefined) {
balance = lamportsToSolString(account.lamports);
}
const base58AccountPubkey = account.pubkey.toBase58();
return (
<tr key={account.id}>
<td>
<span className="badge badge-soft-dark badge-pill">{account.id}</span>
</td>
<td>
<span className={`badge badge-soft-${statusClass}`}>{statusText}</span>
</td>
<td>
<Copyable text={base58AccountPubkey}>
<code>{base58AccountPubkey}</code>
</Copyable>
</td>
<td>{balance}</td>
<td>{data}</td>
<td>
{owner === "-" ? (
owner
) : (
<Copyable text={owner}>
<code>{owner}</code>
</Copyable>
)}
</td>
<td>
<Link
to={location => ({
...location,
pathname: "/account/" + base58AccountPubkey
})}
className="btn btn-rounded-circle btn-white btn-sm"
>
<span className="fe fe-arrow-right"></span>
</Link>
</td>
</tr>
);
};
export default AccountsCard;