Add programs and sysvars to explorer search suggestions (#11467)

This commit is contained in:
Justin Starry
2020-08-08 20:01:26 +08:00
committed by GitHub
parent 40656911a6
commit 3d97b04815
7 changed files with 110 additions and 44 deletions

View File

@ -3,6 +3,7 @@ import bs58 from "bs58";
import { useHistory, useLocation } from "react-router-dom"; import { useHistory, useLocation } from "react-router-dom";
import Select, { InputActionMeta, ActionMeta, ValueType } from "react-select"; import Select, { InputActionMeta, ActionMeta, ValueType } from "react-select";
import StateManager from "react-select"; import StateManager from "react-select";
import { PROGRAM_IDS, SYSVAR_IDS } from "utils/tx";
export function SearchBar() { export function SearchBar() {
const [search, setSearch] = React.useState(""); const [search, setSearch] = React.useState("");
@ -10,10 +11,7 @@ export function SearchBar() {
const history = useHistory(); const history = useHistory();
const location = useLocation(); const location = useLocation();
const onChange = ( const onChange = ({ pathname }: ValueType<any>, meta: ActionMeta<any>) => {
{ value: pathname }: ValueType<any>,
meta: ActionMeta<any>
) => {
if (meta.action === "select-option") { if (meta.action === "select-option") {
history.push({ ...location, pathname }); history.push({ ...location, pathname });
setSearch(""); setSearch("");
@ -24,38 +22,6 @@ export function SearchBar() {
if (action === "input-change") setSearch(value); 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; const resetValue = "" as any;
return ( return (
<div className="container my-4"> <div className="container my-4">
@ -63,7 +29,7 @@ export function SearchBar() {
<div className="col"> <div className="col">
<Select <Select
ref={(ref) => (selectRef.current = ref)} ref={(ref) => (selectRef.current = ref)}
options={options} options={buildOptions(search)}
noOptionsMessage={() => "No Results"} noOptionsMessage={() => "No Results"}
placeholder="Search by address or signature" placeholder="Search by address or signature"
value={resetValue} value={resetValue}
@ -81,6 +47,89 @@ export function SearchBar() {
); );
} }
const SEARCHABLE_PROGRAMS = ["Config", "Stake", "System", "Vote", "Token"];
function buildProgramOptions(search: string) {
const matchedPrograms = Object.entries(PROGRAM_IDS).filter(([, name]) => {
return (
SEARCHABLE_PROGRAMS.includes(name) &&
name.toLowerCase().includes(search.toLowerCase())
);
});
if (matchedPrograms.length > 0) {
return {
label: "Programs",
options: matchedPrograms.map(([id, name]) => ({
label: name,
value: name,
pathname: "/address/" + id,
})),
};
}
}
function buildSysvarOptions(search: string) {
const matchedSysvars = Object.entries(SYSVAR_IDS).filter(([, name]) => {
return name.toLowerCase().includes(search.toLowerCase());
});
if (matchedSysvars.length > 0) {
return {
label: "Sysvars",
options: matchedSysvars.map(([id, name]) => ({
label: name,
value: name,
pathname: "/address/" + id,
})),
};
}
}
function buildOptions(search: string) {
if (search.length === 0) return [];
const options = [];
const programOptions = buildProgramOptions(search);
if (programOptions) {
options.push(programOptions);
}
const sysvarOptions = buildSysvarOptions(search);
if (sysvarOptions) {
options.push(sysvarOptions);
}
try {
const decoded = bs58.decode(search);
if (decoded.length === 32) {
options.push({
label: "Account",
options: [
{
label: search,
value: search,
pathname: "/address/" + search,
},
],
});
} else if (decoded.length === 64) {
options.push({
label: "Transaction",
options: [
{
label: search,
value: search,
pathname: "/tx/" + search,
},
],
});
}
} catch (err) {}
return options;
}
function DropdownIndicator() { function DropdownIndicator() {
return ( return (
<div className="search-indicator"> <div className="search-indicator">

View File

@ -64,7 +64,7 @@ function OverviewCard({
<tr> <tr>
<td>Address</td> <td>Address</td>
<td className="text-lg-right"> <td className="text-lg-right">
<Address pubkey={account.pubkey} alignRight /> <Address pubkey={account.pubkey} alignRight raw />
</td> </td>
</tr> </tr>
<tr> <tr>

View File

@ -18,7 +18,7 @@ export function UnknownAccountCard({ account }: { account: Account }) {
<tr> <tr>
<td>Address</td> <td>Address</td>
<td className="text-lg-right"> <td className="text-lg-right">
<Address pubkey={account.pubkey} alignRight /> <Address pubkey={account.pubkey} alignRight raw />
</td> </td>
</tr> </tr>
<tr> <tr>

View File

@ -10,9 +10,10 @@ type Props = {
pubkey: PublicKey | Pubkey; pubkey: PublicKey | Pubkey;
alignRight?: boolean; alignRight?: boolean;
link?: boolean; link?: boolean;
raw?: boolean;
}; };
export function Address({ pubkey, alignRight, link }: Props) { export function Address({ pubkey, alignRight, link, raw }: Props) {
const [state, setState] = useState<CopyState>("copy"); const [state, setState] = useState<CopyState>("copy");
const address = pubkey.toBase58(); const address = pubkey.toBase58();
@ -36,9 +37,11 @@ export function Address({ pubkey, alignRight, link }: Props) {
<span className="text-monospace"> <span className="text-monospace">
{link ? ( {link ? (
<Link className="" to={clusterPath(`/address/${address}`)}> <Link className="" to={clusterPath(`/address/${address}`)}>
{displayAddress(address)} {raw ? address : displayAddress(address)}
<span className="fe fe-external-link ml-2"></span> <span className="fe fe-external-link ml-2"></span>
</Link> </Link>
) : raw ? (
address
) : ( ) : (
displayAddress(address) displayAddress(address)
)} )}

View File

@ -32,6 +32,14 @@ code, pre {
box-shadow: $card-box-shadow !important; box-shadow: $card-box-shadow !important;
.search-bar__option { .search-bar__option {
cursor: pointer;
&.search-bar__option--is-focused {
background-color: transparent !important;
}
&:hover {
background-color: $gray-700-dark !important; background-color: $gray-700-dark !important;
} }
}
} }

View File

@ -155,6 +155,7 @@ h4.slot-pill {
color: hsl(0,0%,60%); color: hsl(0,0%,60%);
display: flex; display: flex;
padding: 8px 10px; padding: 8px 10px;
padding-left: 14px;
transition: color 150ms; transition: color 150ms;
box-sizing: border-box; box-sizing: border-box;
cursor: pointer; cursor: pointer;
@ -166,6 +167,7 @@ h4.slot-pill {
.search-bar__control { .search-bar__control {
border-radius: $border-radius !important; border-radius: $border-radius !important;
padding: 4px;
} }
.search-bar__menu { .search-bar__menu {

View File

@ -13,7 +13,7 @@ import {
Transaction, Transaction,
} from "@solana/web3.js"; } from "@solana/web3.js";
const PROGRAM_IDS = { export const PROGRAM_IDS = {
Budget1111111111111111111111111111111111111: "Budget", Budget1111111111111111111111111111111111111: "Budget",
Config1111111111111111111111111111111111111: "Config", Config1111111111111111111111111111111111111: "Config",
Exchange11111111111111111111111111111111111: "Exchange", Exchange11111111111111111111111111111111111: "Exchange",
@ -31,8 +31,11 @@ const LOADER_IDS = {
[BpfLoader.programId.toBase58()]: "BPF Loader", [BpfLoader.programId.toBase58()]: "BPF Loader",
}; };
const SYSVAR_IDS = { const SYSVAR_ID: { [key: string]: string } = {
Sysvar1111111111111111111111111111111111111: "SYSVAR", Sysvar1111111111111111111111111111111111111: "SYSVAR",
};
export const SYSVAR_IDS = {
[SYSVAR_CLOCK_PUBKEY.toBase58()]: "SYSVAR_CLOCK", [SYSVAR_CLOCK_PUBKEY.toBase58()]: "SYSVAR_CLOCK",
SysvarEpochSchedu1e111111111111111111111111: "SYSVAR_EPOCH_SCHEDULE", SysvarEpochSchedu1e111111111111111111111111: "SYSVAR_EPOCH_SCHEDULE",
SysvarFees111111111111111111111111111111111: "SYSVAR_FEES", SysvarFees111111111111111111111111111111111: "SYSVAR_FEES",
@ -49,6 +52,7 @@ export function displayAddress(address: string): string {
PROGRAM_IDS[address] || PROGRAM_IDS[address] ||
LOADER_IDS[address] || LOADER_IDS[address] ||
SYSVAR_IDS[address] || SYSVAR_IDS[address] ||
SYSVAR_ID[address] ||
address address
); );
} }