Add unified search bar to the explorer
This commit is contained in:
committed by
Justin Starry
parent
d4eb49d252
commit
0d8f3139ae
181
explorer/package-lock.json
generated
181
explorer/package-lock.json
generated
@ -2011,6 +2011,87 @@
|
||||
"resolved": "https://registry.npmjs.org/@csstools/normalize.css/-/normalize.css-10.1.0.tgz",
|
||||
"integrity": "sha512-ij4wRiunFfaJxjB0BdrYHIH8FxBJpOwNPhhAcunlmPdXudL1WQV1qoP9un6JsEBAgQH+7UXyyjh0g7jTxXK6tg=="
|
||||
},
|
||||
"@emotion/cache": {
|
||||
"version": "10.0.29",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-10.0.29.tgz",
|
||||
"integrity": "sha512-fU2VtSVlHiF27empSbxi1O2JFdNWZO+2NFHfwO0pxgTep6Xa3uGb+3pVKfLww2l/IBGLNEZl5Xf/++A4wAYDYQ==",
|
||||
"requires": {
|
||||
"@emotion/sheet": "0.9.4",
|
||||
"@emotion/stylis": "0.8.5",
|
||||
"@emotion/utils": "0.11.3",
|
||||
"@emotion/weak-memoize": "0.2.5"
|
||||
}
|
||||
},
|
||||
"@emotion/core": {
|
||||
"version": "10.0.28",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/core/-/core-10.0.28.tgz",
|
||||
"integrity": "sha512-pH8UueKYO5jgg0Iq+AmCLxBsvuGtvlmiDCOuv8fGNYn3cowFpLN98L8zO56U0H1PjDIyAlXymgL3Wu7u7v6hbA==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.5.5",
|
||||
"@emotion/cache": "^10.0.27",
|
||||
"@emotion/css": "^10.0.27",
|
||||
"@emotion/serialize": "^0.11.15",
|
||||
"@emotion/sheet": "0.9.4",
|
||||
"@emotion/utils": "0.11.3"
|
||||
}
|
||||
},
|
||||
"@emotion/css": {
|
||||
"version": "10.0.27",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/css/-/css-10.0.27.tgz",
|
||||
"integrity": "sha512-6wZjsvYeBhyZQYNrGoR5yPMYbMBNEnanDrqmsqS1mzDm1cOTu12shvl2j4QHNS36UaTE0USIJawCH9C8oW34Zw==",
|
||||
"requires": {
|
||||
"@emotion/serialize": "^0.11.15",
|
||||
"@emotion/utils": "0.11.3",
|
||||
"babel-plugin-emotion": "^10.0.27"
|
||||
}
|
||||
},
|
||||
"@emotion/hash": {
|
||||
"version": "0.8.0",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz",
|
||||
"integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow=="
|
||||
},
|
||||
"@emotion/memoize": {
|
||||
"version": "0.7.4",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.4.tgz",
|
||||
"integrity": "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw=="
|
||||
},
|
||||
"@emotion/serialize": {
|
||||
"version": "0.11.16",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-0.11.16.tgz",
|
||||
"integrity": "sha512-G3J4o8by0VRrO+PFeSc3js2myYNOXVJ3Ya+RGVxnshRYgsvErfAOglKAiy1Eo1vhzxqtUvjCyS5gtewzkmvSSg==",
|
||||
"requires": {
|
||||
"@emotion/hash": "0.8.0",
|
||||
"@emotion/memoize": "0.7.4",
|
||||
"@emotion/unitless": "0.7.5",
|
||||
"@emotion/utils": "0.11.3",
|
||||
"csstype": "^2.5.7"
|
||||
}
|
||||
},
|
||||
"@emotion/sheet": {
|
||||
"version": "0.9.4",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-0.9.4.tgz",
|
||||
"integrity": "sha512-zM9PFmgVSqBw4zL101Q0HrBVTGmpAxFZH/pYx/cjJT5advXguvcgjHFTCaIO3enL/xr89vK2bh0Mfyj9aa0ANA=="
|
||||
},
|
||||
"@emotion/stylis": {
|
||||
"version": "0.8.5",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/stylis/-/stylis-0.8.5.tgz",
|
||||
"integrity": "sha512-h6KtPihKFn3T9fuIrwvXXUOwlx3rfUvfZIcP5a6rh8Y7zjE3O06hT5Ss4S/YI1AYhuZ1kjaE/5EaOOI2NqSylQ=="
|
||||
},
|
||||
"@emotion/unitless": {
|
||||
"version": "0.7.5",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz",
|
||||
"integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg=="
|
||||
},
|
||||
"@emotion/utils": {
|
||||
"version": "0.11.3",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-0.11.3.tgz",
|
||||
"integrity": "sha512-0o4l6pZC+hI88+bzuaX/6BgOvQVhbt2PfmxauVaYOGgbsAw14wdKyvMCZXnsnsHys94iadcF+RG/wZyx6+ZZBw=="
|
||||
},
|
||||
"@emotion/weak-memoize": {
|
||||
"version": "0.2.5",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.2.5.tgz",
|
||||
"integrity": "sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA=="
|
||||
},
|
||||
"@hapi/address": {
|
||||
"version": "2.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@hapi/address/-/address-2.1.4.tgz",
|
||||
@ -3026,6 +3107,24 @@
|
||||
"@types/react-router": "*"
|
||||
}
|
||||
},
|
||||
"@types/react-select": {
|
||||
"version": "3.0.15",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-select/-/react-select-3.0.15.tgz",
|
||||
"integrity": "sha512-yPmkr6zgVFR95JqBtkVaVDK/u1jdbTw8c8n9h5zWY/481IoBKZgrvOHweJXGvnOJ43umH7ImcT5m/uj5uESMBQ==",
|
||||
"requires": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"@types/react-transition-group": "*"
|
||||
}
|
||||
},
|
||||
"@types/react-transition-group": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.0.tgz",
|
||||
"integrity": "sha512-/QfLHGpu+2fQOqQaXh8MG9q03bFENooTb/it4jr5kKaZlDQfWvjqWZg48AwzPVMBHlRuTRAY7hRHCEOXz5kV6w==",
|
||||
"requires": {
|
||||
"@types/react": "*"
|
||||
}
|
||||
},
|
||||
"@types/socket.io-client": {
|
||||
"version": "1.4.33",
|
||||
"resolved": "https://registry.npmjs.org/@types/socket.io-client/-/socket.io-client-1.4.33.tgz",
|
||||
@ -3871,6 +3970,30 @@
|
||||
"object.assign": "^4.1.0"
|
||||
}
|
||||
},
|
||||
"babel-plugin-emotion": {
|
||||
"version": "10.0.33",
|
||||
"resolved": "https://registry.npmjs.org/babel-plugin-emotion/-/babel-plugin-emotion-10.0.33.tgz",
|
||||
"integrity": "sha512-bxZbTTGz0AJQDHm8k6Rf3RQJ8tX2scsfsRyKVgAbiUPUNIRtlK+7JxP+TAd1kRLABFxe0CFm2VdK4ePkoA9FxQ==",
|
||||
"requires": {
|
||||
"@babel/helper-module-imports": "^7.0.0",
|
||||
"@emotion/hash": "0.8.0",
|
||||
"@emotion/memoize": "0.7.4",
|
||||
"@emotion/serialize": "^0.11.16",
|
||||
"babel-plugin-macros": "^2.0.0",
|
||||
"babel-plugin-syntax-jsx": "^6.18.0",
|
||||
"convert-source-map": "^1.5.0",
|
||||
"escape-string-regexp": "^1.0.5",
|
||||
"find-root": "^1.1.0",
|
||||
"source-map": "^0.5.7"
|
||||
},
|
||||
"dependencies": {
|
||||
"source-map": {
|
||||
"version": "0.5.7",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
|
||||
"integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w="
|
||||
}
|
||||
}
|
||||
},
|
||||
"babel-plugin-istanbul": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-5.2.0.tgz",
|
||||
@ -3994,6 +4117,11 @@
|
||||
"resolved": "https://registry.npmjs.org/babel-plugin-named-asset-import/-/babel-plugin-named-asset-import-0.3.6.tgz",
|
||||
"integrity": "sha512-1aGDUfL1qOOIoqk9QKGIo2lANk+C7ko/fqH0uIyC71x3PEGz0uVP8ISgfEsFuG+FKmjHTvFK/nNM8dowpmUxLA=="
|
||||
},
|
||||
"babel-plugin-syntax-jsx": {
|
||||
"version": "6.18.0",
|
||||
"resolved": "https://registry.npmjs.org/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz",
|
||||
"integrity": "sha1-CvMqmm4Tyno/1QaeYtew9Y0NiUY="
|
||||
},
|
||||
"babel-plugin-syntax-object-rest-spread": {
|
||||
"version": "6.13.0",
|
||||
"resolved": "https://registry.npmjs.org/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz",
|
||||
@ -6224,6 +6352,15 @@
|
||||
"utila": "~0.4"
|
||||
}
|
||||
},
|
||||
"dom-helpers": {
|
||||
"version": "5.1.4",
|
||||
"resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.1.4.tgz",
|
||||
"integrity": "sha512-TjMyeVUvNEnOnhzs6uAn9Ya47GmMo3qq7m+Lr/3ON0Rs5kHvb8I+SQYjLUSYn7qhEm0QjW0yrBkvz9yOrwwz1A==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.8.7",
|
||||
"csstype": "^2.6.7"
|
||||
}
|
||||
},
|
||||
"dom-serializer": {
|
||||
"version": "0.2.2",
|
||||
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz",
|
||||
@ -7503,6 +7640,11 @@
|
||||
"pkg-dir": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"find-root": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz",
|
||||
"integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng=="
|
||||
},
|
||||
"find-up": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz",
|
||||
@ -10292,6 +10434,11 @@
|
||||
"p-is-promise": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"memoize-one": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.1.1.tgz",
|
||||
"integrity": "sha512-HKeeBpWvqiVJD57ZUAsJNm71eHTykffzcLZVYWiVfQeI1rJtuEaS7hQiEpWfVVk18donPwJEcFKIkCmPJNOhHA=="
|
||||
},
|
||||
"memory-fs": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz",
|
||||
@ -13231,6 +13378,14 @@
|
||||
"resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.7.tgz",
|
||||
"integrity": "sha512-TAv1KJFh3RhqxNvhzxj6LeT5NWklP6rDr2a0jaTfsZ5wSZWHOGeqQyejUp3xxLfPt2UpyJEcVQB/zyPcmonNFA=="
|
||||
},
|
||||
"react-input-autosize": {
|
||||
"version": "2.2.2",
|
||||
"resolved": "https://registry.npmjs.org/react-input-autosize/-/react-input-autosize-2.2.2.tgz",
|
||||
"integrity": "sha512-jQJgYCA3S0j+cuOwzuCd1OjmBmnZLdqQdiLKRYrsMMzbjUrVDS5RvJUDwJqA7sKuksDuzFtm6hZGKFu7Mjk5aw==",
|
||||
"requires": {
|
||||
"prop-types": "^15.5.8"
|
||||
}
|
||||
},
|
||||
"react-is": {
|
||||
"version": "16.13.0",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.0.tgz",
|
||||
@ -13543,6 +13698,32 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"react-select": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/react-select/-/react-select-3.1.0.tgz",
|
||||
"integrity": "sha512-wBFVblBH1iuCBprtpyGtd1dGMadsG36W5/t2Aj8OE6WbByDg5jIFyT7X5gT+l0qmT5TqWhxX+VsKJvCEl2uL9g==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.4.4",
|
||||
"@emotion/cache": "^10.0.9",
|
||||
"@emotion/core": "^10.0.9",
|
||||
"@emotion/css": "^10.0.9",
|
||||
"memoize-one": "^5.0.0",
|
||||
"prop-types": "^15.6.0",
|
||||
"react-input-autosize": "^2.2.2",
|
||||
"react-transition-group": "^4.3.0"
|
||||
}
|
||||
},
|
||||
"react-transition-group": {
|
||||
"version": "4.4.1",
|
||||
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.1.tgz",
|
||||
"integrity": "sha512-Djqr7OQ2aPUiYurhPalTrVy9ddmFCCzwhqQmtN+J3+3DzLO209Fdr70QrN8Z3DsglWql6iY1lDWAfpFiBtuKGw==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.5.5",
|
||||
"dom-helpers": "^5.0.1",
|
||||
"loose-envify": "^1.4.0",
|
||||
"prop-types": "^15.6.2"
|
||||
}
|
||||
},
|
||||
"read-pkg": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz",
|
||||
|
@ -14,6 +14,7 @@
|
||||
"@types/react": "^16.9.43",
|
||||
"@types/react-dom": "^16.9.8",
|
||||
"@types/react-router-dom": "^5.1.5",
|
||||
"@types/react-select": "^3.0.15",
|
||||
"@types/socket.io-client": "^1.4.33",
|
||||
"bootstrap": "^4.5.0",
|
||||
"bs58": "^4.0.1",
|
||||
@ -26,6 +27,7 @@
|
||||
"react-dom": "^16.13.1",
|
||||
"react-router-dom": "^5.2.0",
|
||||
"react-scripts": "3.4.1",
|
||||
"react-select": "^3.1.0",
|
||||
"socket.io-client": "^2.3.0",
|
||||
"solana-sdk-wasm": "file:wasm/pkg",
|
||||
"superstruct": "^0.10.12",
|
||||
|
@ -12,6 +12,7 @@ import StatsCard from "components/StatsCard";
|
||||
import MessageBanner from "components/MessageBanner";
|
||||
import Navbar from "components/Navbar";
|
||||
import { ClusterStatusBanner } from "components/ClusterStatusButton";
|
||||
import { SearchBar } from "components/SearchBar";
|
||||
|
||||
function App() {
|
||||
return (
|
||||
@ -21,6 +22,7 @@ function App() {
|
||||
<Navbar />
|
||||
<MessageBanner />
|
||||
<ClusterStatusBanner />
|
||||
<SearchBar />
|
||||
<Switch>
|
||||
<Route exact path={["/supply", "/accounts", "accounts/top"]}>
|
||||
<div className="container mt-4">
|
||||
|
@ -1,7 +1,6 @@
|
||||
import React from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import { PublicKey, StakeProgram } from "@solana/web3.js";
|
||||
import { useHistory, useLocation } from "react-router-dom";
|
||||
import {
|
||||
FetchStatus,
|
||||
useFetchAccountInfo,
|
||||
@ -21,9 +20,6 @@ import { useFetchAccountHistory } from "providers/accounts/history";
|
||||
type Props = { address: string };
|
||||
export default function AccountDetails({ address }: Props) {
|
||||
const fetchAccount = useFetchAccountInfo();
|
||||
const [search, setSearch] = React.useState(address);
|
||||
const history = useHistory();
|
||||
const location = useLocation();
|
||||
|
||||
let pubkey: PublicKey | undefined;
|
||||
try {
|
||||
@ -33,35 +29,17 @@ export default function AccountDetails({ address }: Props) {
|
||||
// TODO handle bad addresses
|
||||
}
|
||||
|
||||
const updateAddress = () => {
|
||||
history.push({ ...location, pathname: "/account/" + search });
|
||||
};
|
||||
|
||||
// Fetch account on load
|
||||
React.useEffect(() => {
|
||||
setSearch(address);
|
||||
if (pubkey) fetchAccount(pubkey);
|
||||
}, [address]); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
|
||||
const searchInput = (
|
||||
<input
|
||||
type="text"
|
||||
value={search}
|
||||
onChange={(e) => setSearch(e.target.value)}
|
||||
onKeyUp={(e) => e.key === "Enter" && updateAddress()}
|
||||
className="form-control form-control-prepended search text-monospace"
|
||||
placeholder="Search for address"
|
||||
/>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="container">
|
||||
<div className="container mt-n3">
|
||||
<div className="header">
|
||||
<div className="header-body">
|
||||
<h6 className="header-pretitle">Address</h6>
|
||||
<h4 className="header-title text-monospace text-truncate font-weight-bold">
|
||||
{address}
|
||||
</h4>
|
||||
<h6 className="header-pretitle">Details</h6>
|
||||
<h4 className="header-title">Account</h4>
|
||||
</div>
|
||||
</div>
|
||||
{pubkey && <AccountCards pubkey={pubkey} />}
|
||||
@ -100,10 +78,18 @@ function UnknownAccountCard({ account }: { account: Account }) {
|
||||
return (
|
||||
<div className="card">
|
||||
<div className="card-header align-items-center">
|
||||
<h3 className="card-header-title">Account Overview</h3>
|
||||
<h3 className="card-header-title">Overview</h3>
|
||||
</div>
|
||||
|
||||
<TableCardBody>
|
||||
<tr>
|
||||
<td>Address</td>
|
||||
<td className="text-right">
|
||||
<Copyable text={account.pubkey.toBase58()} right bottom>
|
||||
<code>{displayAddress(account.pubkey.toBase58())}</code>
|
||||
</Copyable>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Balance (SOL)</td>
|
||||
<td className="text-right text-uppercase">
|
||||
|
@ -1,17 +1,9 @@
|
||||
import React from "react";
|
||||
import Logo from "img/logos-solana/light-explorer-logo.svg";
|
||||
import { Location } from "history";
|
||||
import { pickCluster } from "utils/url";
|
||||
import { clusterPath } from "utils/url";
|
||||
import { Link, NavLink } from "react-router-dom";
|
||||
import { ClusterStatusButton } from "components/ClusterStatusButton";
|
||||
|
||||
const clusterPath = (pathname: string) => {
|
||||
return (location: Location) => ({
|
||||
...pickCluster(location),
|
||||
pathname,
|
||||
});
|
||||
};
|
||||
|
||||
export default function Navbar() {
|
||||
// TODO: use `collapsing` to animate collapsible navbar
|
||||
const [collapse, setCollapse] = React.useState(false);
|
||||
@ -31,11 +23,15 @@ export default function Navbar() {
|
||||
<span className="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
|
||||
<div className={`collapse navbar-collapse ml-auto mr-4 ${collapse ? "show" : ""}`}>
|
||||
<div
|
||||
className={`collapse navbar-collapse ml-auto mr-4 ${
|
||||
collapse ? "show" : ""
|
||||
}`}
|
||||
>
|
||||
<ul className="navbar-nav mr-auto">
|
||||
<li className="nav-item">
|
||||
<NavLink className="nav-link" to={clusterPath("/")} exact>
|
||||
Cluster Status
|
||||
Cluster Stats
|
||||
</NavLink>
|
||||
</li>
|
||||
<li className="nav-item">
|
||||
|
92
explorer/src/components/SearchBar.tsx
Normal file
92
explorer/src/components/SearchBar.tsx
Normal file
@ -0,0 +1,92 @@
|
||||
import React from "react";
|
||||
import bs58 from "bs58";
|
||||
import { useHistory, useLocation } from "react-router-dom";
|
||||
import Select, { InputActionMeta, ActionMeta, ValueType } from "react-select";
|
||||
import StateManager from "react-select";
|
||||
|
||||
export function SearchBar() {
|
||||
const [search, setSearch] = React.useState("");
|
||||
const selectRef = React.useRef<StateManager<any> | null>(null);
|
||||
const history = useHistory();
|
||||
const location = useLocation();
|
||||
|
||||
const onChange = (
|
||||
{ value: pathname }: ValueType<any>,
|
||||
meta: ActionMeta<any>
|
||||
) => {
|
||||
if (meta.action === "select-option") {
|
||||
history.push({ ...location, pathname });
|
||||
setSearch("");
|
||||
}
|
||||
};
|
||||
|
||||
const onInputChange = (value: string, { action }: InputActionMeta) => {
|
||||
if (action === "input-change") setSearch(value);
|
||||
};
|
||||
|
||||
const options = ((searchValue: string) => {
|
||||
try {
|
||||
const decoded = bs58.decode(searchValue);
|
||||
if (decoded.length === 32) {
|
||||
return [
|
||||
{
|
||||
label: "Account",
|
||||
options: [
|
||||
{
|
||||
label: searchValue,
|
||||
value: "/address/" + searchValue,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
} else if (decoded.length === 64) {
|
||||
return [
|
||||
{
|
||||
label: "Transaction",
|
||||
options: [
|
||||
{
|
||||
label: searchValue,
|
||||
value: "/tx/" + searchValue,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
}
|
||||
} catch (err) {}
|
||||
return [];
|
||||
})(search);
|
||||
|
||||
const resetValue = "" as any;
|
||||
return (
|
||||
<div className="container my-4">
|
||||
<div className="row align-items-center">
|
||||
<div className="col">
|
||||
<Select
|
||||
ref={(ref) => (selectRef.current = ref)}
|
||||
options={options}
|
||||
noOptionsMessage={() => "No Results"}
|
||||
placeholder="Search by address or signature"
|
||||
value={resetValue}
|
||||
inputValue={search}
|
||||
blurInputOnSelect
|
||||
onMenuClose={() => selectRef.current?.blur()}
|
||||
onChange={onChange}
|
||||
onInputChange={onInputChange}
|
||||
components={{ DropdownIndicator }}
|
||||
styles={{
|
||||
control: (styles) => ({ ...styles, borderRadius: "7px" }),
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function DropdownIndicator() {
|
||||
return (
|
||||
<div className="search-indicator">
|
||||
<span className="fe fe-search"></span>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -19,7 +19,7 @@ export default function StatsCard() {
|
||||
<div className="card-header">
|
||||
<div className="row align-items-center">
|
||||
<div className="col">
|
||||
<h4 className="card-header-title">Live Cluster Status</h4>
|
||||
<h4 className="card-header-title">Live Cluster Stats</h4>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -36,13 +36,11 @@ export default function TransactionDetails({ signature }: Props) {
|
||||
}, [signature]); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
|
||||
return (
|
||||
<div className="container">
|
||||
<div className="container mt-n3">
|
||||
<div className="header">
|
||||
<div className="header-body">
|
||||
<h6 className="header-pretitle">Transaction</h6>
|
||||
<h4 className="header-title text-monospace text-truncate font-weight-bold">
|
||||
{signature}
|
||||
</h4>
|
||||
<h6 className="header-pretitle">Details</h6>
|
||||
<h4 className="header-title">Transaction</h4>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -103,7 +101,7 @@ function StatusCard({ signature }: Props) {
|
||||
return (
|
||||
<div className="card">
|
||||
<div className="card-header align-items-center">
|
||||
<h3 className="card-header-title">Status</h3>
|
||||
<h3 className="card-header-title">Overview</h3>
|
||||
<button
|
||||
className="btn btn-white btn-sm"
|
||||
onClick={() => refresh(signature)}
|
||||
@ -114,6 +112,15 @@ function StatusCard({ signature }: Props) {
|
||||
</div>
|
||||
|
||||
<TableCardBody>
|
||||
<tr>
|
||||
<td>Signature</td>
|
||||
<td className="text-right">
|
||||
<Copyable text={signature} right bottom>
|
||||
<code>{signature}</code>
|
||||
</Copyable>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Result</td>
|
||||
<td className="text-right">{renderResult()}</td>
|
||||
|
@ -1,15 +1,7 @@
|
||||
import React from "react";
|
||||
import io from "socket.io-client";
|
||||
|
||||
import {
|
||||
object,
|
||||
number,
|
||||
is,
|
||||
StructType,
|
||||
array,
|
||||
nullable,
|
||||
any,
|
||||
} from "superstruct";
|
||||
import { object, number, is, StructType, any } from "superstruct";
|
||||
import { useCluster, Cluster } from "providers/cluster";
|
||||
|
||||
// TODO: use `partial` when it is fixed
|
||||
@ -51,11 +43,7 @@ export const PERF_UPDATE_SEC = 5;
|
||||
// https://github.com/ianstormtaylor/superstruct/issues/405
|
||||
const PerformanceInfo = object({
|
||||
avgTPS: number(),
|
||||
perfHistory: object({
|
||||
s: array(nullable(number())),
|
||||
m: array(nullable(number())),
|
||||
l: array(nullable(number())),
|
||||
}),
|
||||
perfHistory: any(),
|
||||
totalTransactionCount: number(),
|
||||
});
|
||||
|
||||
|
@ -150,3 +150,16 @@ h4.slot-pill {
|
||||
.line-height-md {
|
||||
line-height: 1.5rem;
|
||||
}
|
||||
|
||||
.search-indicator {
|
||||
color: hsl(0,0%,60%);
|
||||
display: flex;
|
||||
padding: 8px 10px;
|
||||
transition: color 150ms;
|
||||
box-sizing: border-box;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
color: hsl(0,0%,40%);
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,13 @@ export function useQuery() {
|
||||
return new URLSearchParams(useLocation().search);
|
||||
}
|
||||
|
||||
export const clusterPath = (pathname: string) => {
|
||||
return (location: Location) => ({
|
||||
...pickCluster(location),
|
||||
pathname,
|
||||
});
|
||||
};
|
||||
|
||||
export function pickCluster(location: Location): Location {
|
||||
const cluster = new URLSearchParams(location.search).get("cluster");
|
||||
|
||||
|
Reference in New Issue
Block a user