Rename network to cluster

This commit is contained in:
Justin Starry
2020-03-31 14:36:40 +08:00
committed by Michael Vines
parent c47c3acb10
commit 1d7dbe859a
6 changed files with 112 additions and 112 deletions

View File

@ -1,17 +1,17 @@
import React from "react"; import React from "react";
import { NetworkProvider } from "./providers/network"; import { ClusterProvider } from "./providers/cluster";
import { TransactionsProvider } from "./providers/transactions"; import { TransactionsProvider } from "./providers/transactions";
import NetworkStatusButton from "./components/NetworkStatusButton"; import ClusterStatusButton from "./components/ClusterStatusButton";
import TransactionsCard from "./components/TransactionsCard"; import TransactionsCard from "./components/TransactionsCard";
import NetworkModal from "./components/NetworkModal"; import ClusterModal from "./components/ClusterModal";
import Logo from "./img/logos-solana/light-explorer-logo.svg"; import Logo from "./img/logos-solana/light-explorer-logo.svg";
function App() { function App() {
const [showModal, setShowModal] = React.useState(false); const [showModal, setShowModal] = React.useState(false);
return ( return (
<NetworkProvider> <ClusterProvider>
<NetworkModal show={showModal} onClose={() => setShowModal(false)} /> <ClusterModal show={showModal} onClose={() => setShowModal(false)} />
<div className="main-content"> <div className="main-content">
<div className="header"> <div className="header">
<div className="container"> <div className="container">
@ -21,7 +21,7 @@ function App() {
<img src={Logo} width="250" alt="Solana Explorer" /> <img src={Logo} width="250" alt="Solana Explorer" />
</div> </div>
<div className="col-auto"> <div className="col-auto">
<NetworkStatusButton onClick={() => setShowModal(true)} /> <ClusterStatusButton onClick={() => setShowModal(true)} />
</div> </div>
</div> </div>
</div> </div>
@ -39,7 +39,7 @@ function App() {
</div> </div>
</div> </div>
<Overlay show={showModal} onClick={() => setShowModal(false)} /> <Overlay show={showModal} onClick={() => setShowModal(false)} />
</NetworkProvider> </ClusterProvider>
); );
} }

View File

@ -1,14 +1,14 @@
import React from "react"; import React from "react";
import { import {
useNetwork, useCluster,
useNetworkDispatch, useClusterDispatch,
updateNetwork, updateCluster,
NetworkStatus, ClusterStatus,
networkUrl, clusterUrl,
networkName, clusterName,
NETWORKS, CLUSTERS,
Network Cluster
} from "../providers/network"; } from "../providers/cluster";
import { assertUnreachable } from "../utils"; import { assertUnreachable } from "../utils";
type Props = { type Props = {
@ -16,7 +16,7 @@ type Props = {
onClose: () => void; onClose: () => void;
}; };
function NetworkModal({ show, onClose }: Props) { function ClusterModal({ show, onClose }: Props) {
return ( return (
<div <div
className={`modal fade fixed-right${show ? " show" : ""}`} className={`modal fade fixed-right${show ? " show" : ""}`}
@ -31,7 +31,7 @@ function NetworkModal({ show, onClose }: Props) {
<h2 className="text-center mb-4 mt-4">Choose a Cluster</h2> <h2 className="text-center mb-4 mt-4">Choose a Cluster</h2>
<NetworkToggle /> <ClusterToggle />
</div> </div>
</div> </div>
</div> </div>
@ -40,9 +40,9 @@ function NetworkModal({ show, onClose }: Props) {
} }
type InputProps = { activeSuffix: string; active: boolean }; type InputProps = { activeSuffix: string; active: boolean };
function CustomNetworkInput({ activeSuffix, active }: InputProps) { function CustomClusterInput({ activeSuffix, active }: InputProps) {
const { customUrl } = useNetwork(); const { customUrl } = useCluster();
const dispatch = useNetworkDispatch(); const dispatch = useClusterDispatch();
const [editing, setEditing] = React.useState(false); const [editing, setEditing] = React.useState(false);
const customClass = (prefix: string) => const customClass = (prefix: string) =>
@ -53,7 +53,7 @@ function CustomNetworkInput({ activeSuffix, active }: InputProps) {
<div <div
className="btn input-group input-group-merge p-0" className="btn input-group input-group-merge p-0"
onClick={() => onClick={() =>
!active && updateNetwork(dispatch, Network.Custom, customUrl) !active && updateCluster(dispatch, Cluster.Custom, customUrl)
} }
> >
<input <input
@ -65,7 +65,7 @@ function CustomNetworkInput({ activeSuffix, active }: InputProps) {
onFocus={() => setEditing(true)} onFocus={() => setEditing(true)}
onBlur={() => setEditing(false)} onBlur={() => setEditing(false)}
onInput={e => onInput={e =>
updateNetwork(dispatch, Network.Custom, e.currentTarget.value) updateCluster(dispatch, Cluster.Custom, e.currentTarget.value)
} }
/> />
<div className="input-group-prepend"> <div className="input-group-prepend">
@ -77,19 +77,19 @@ function CustomNetworkInput({ activeSuffix, active }: InputProps) {
); );
} }
function NetworkToggle() { function ClusterToggle() {
const { status, network, customUrl } = useNetwork(); const { status, cluster, customUrl } = useCluster();
const dispatch = useNetworkDispatch(); const dispatch = useClusterDispatch();
let activeSuffix = ""; let activeSuffix = "";
switch (status) { switch (status) {
case NetworkStatus.Connected: case ClusterStatus.Connected:
activeSuffix = "success"; activeSuffix = "success";
break; break;
case NetworkStatus.Connecting: case ClusterStatus.Connecting:
activeSuffix = "warning"; activeSuffix = "warning";
break; break;
case NetworkStatus.Failure: case ClusterStatus.Failure:
activeSuffix = "danger"; activeSuffix = "danger";
break; break;
default: default:
@ -98,11 +98,11 @@ function NetworkToggle() {
return ( return (
<div className="btn-group-toggle d-flex flex-wrap mb-4"> <div className="btn-group-toggle d-flex flex-wrap mb-4">
{NETWORKS.map((net, index) => { {CLUSTERS.map((net, index) => {
const active = net === network; const active = net === cluster;
if (net === Network.Custom) if (net === Cluster.Custom)
return ( return (
<CustomNetworkInput <CustomClusterInput
key={index} key={index}
activeSuffix={activeSuffix} activeSuffix={activeSuffix}
active={active} active={active}
@ -121,11 +121,11 @@ function NetworkToggle() {
<input <input
type="radio" type="radio"
checked={active} checked={active}
onChange={() => updateNetwork(dispatch, net, customUrl)} onChange={() => updateCluster(dispatch, net, customUrl)}
/> />
{`${networkName(net)}: `} {`${clusterName(net)}: `}
<span className="text-muted d-inline-block"> <span className="text-muted d-inline-block">
{networkUrl(net, customUrl)} {clusterUrl(net, customUrl)}
</span> </span>
</label> </label>
); );
@ -134,4 +134,4 @@ function NetworkToggle() {
); );
} }
export default NetworkModal; export default ClusterModal;

View File

@ -1,7 +1,7 @@
import React from "react"; import React from "react";
import { useNetwork, NetworkStatus, Network } from "../providers/network"; import { useCluster, ClusterStatus, Cluster } from "../providers/cluster";
function NetworkStatusButton({ onClick }: { onClick: () => void }) { function ClusterStatusButton({ onClick }: { onClick: () => void }) {
return ( return (
<div onClick={onClick}> <div onClick={onClick}>
<Button /> <Button />
@ -10,11 +10,11 @@ function NetworkStatusButton({ onClick }: { onClick: () => void }) {
} }
function Button() { function Button() {
const { status, network, name, customUrl } = useNetwork(); const { status, cluster, name, customUrl } = useCluster();
const statusName = network !== Network.Custom ? `${name}` : `${customUrl}`; const statusName = cluster !== Cluster.Custom ? `${name}` : `${customUrl}`;
switch (status) { switch (status) {
case NetworkStatus.Connected: case ClusterStatus.Connected:
return ( return (
<span className="btn btn-outline-success lift"> <span className="btn btn-outline-success lift">
<span className="fe fe-check-circle mr-2"></span> <span className="fe fe-check-circle mr-2"></span>
@ -22,7 +22,7 @@ function Button() {
</span> </span>
); );
case NetworkStatus.Connecting: case ClusterStatus.Connecting:
return ( return (
<span className="btn btn-outline-warning lift"> <span className="btn btn-outline-warning lift">
<span <span
@ -34,7 +34,7 @@ function Button() {
</span> </span>
); );
case NetworkStatus.Failure: case ClusterStatus.Failure:
return ( return (
<span className="btn btn-outline-danger lift"> <span className="btn btn-outline-danger lift">
<span className="fe fe-alert-circle mr-2"></span> <span className="fe fe-alert-circle mr-2"></span>
@ -44,4 +44,4 @@ function Button() {
} }
} }
export default NetworkStatusButton; export default ClusterStatusButton;

View File

@ -9,14 +9,14 @@ import {
} from "../providers/transactions"; } from "../providers/transactions";
import bs58 from "bs58"; import bs58 from "bs58";
import { assertUnreachable } from "../utils"; import { assertUnreachable } from "../utils";
import { useNetwork } from "../providers/network"; import { useCluster } from "../providers/cluster";
function TransactionsCard() { function TransactionsCard() {
const { transactions, idCounter } = useTransactions(); const { transactions, idCounter } = useTransactions();
const dispatch = useTransactionsDispatch(); const dispatch = useTransactionsDispatch();
const signatureInput = React.useRef<HTMLInputElement>(null); const signatureInput = React.useRef<HTMLInputElement>(null);
const [error, setError] = React.useState(""); const [error, setError] = React.useState("");
const { url } = useNetwork(); const { url } = useCluster();
const onNew = (signature: string) => { const onNew = (signature: string) => {
if (signature.length === 0) return; if (signature.length === 0) return;
@ -115,7 +115,7 @@ const renderTransactionRow = (transaction: Transaction) => {
switch (transaction.status) { switch (transaction.status) {
case Status.CheckFailed: case Status.CheckFailed:
statusClass = "dark"; statusClass = "dark";
statusText = "Network Error"; statusText = "Cluster Error";
break; break;
case Status.Checking: case Status.Checking:
statusClass = "info"; statusClass = "info";

View File

@ -2,35 +2,35 @@ import React from "react";
import { testnetChannelEndpoint, Connection } from "@solana/web3.js"; import { testnetChannelEndpoint, Connection } from "@solana/web3.js";
import { findGetParameter } from "../utils"; import { findGetParameter } from "../utils";
export enum NetworkStatus { export enum ClusterStatus {
Connected, Connected,
Connecting, Connecting,
Failure Failure
} }
export enum Network { export enum Cluster {
MainnetBeta, MainnetBeta,
Testnet, Testnet,
Devnet, Devnet,
Custom Custom
} }
export const NETWORKS = [ export const CLUSTERS = [
Network.MainnetBeta, Cluster.MainnetBeta,
Network.Testnet, Cluster.Testnet,
Network.Devnet, Cluster.Devnet,
Network.Custom Cluster.Custom
]; ];
export function networkName(network: Network): string { export function clusterName(cluster: Cluster): string {
switch (network) { switch (cluster) {
case Network.MainnetBeta: case Cluster.MainnetBeta:
return "Mainnet Beta"; return "Mainnet Beta";
case Network.Testnet: case Cluster.Testnet:
return "Testnet"; return "Testnet";
case Network.Devnet: case Cluster.Devnet:
return "Devnet"; return "Devnet";
case Network.Custom: case Cluster.Custom:
return "Custom"; return "Custom";
} }
} }
@ -39,39 +39,39 @@ export const MAINNET_BETA_URL = "https://api.mainnet-beta.solana.com";
export const TESTNET_URL = "https://testnet.solana.com"; export const TESTNET_URL = "https://testnet.solana.com";
export const DEVNET_URL = testnetChannelEndpoint("stable"); export const DEVNET_URL = testnetChannelEndpoint("stable");
export const DEFAULT_NETWORK = Network.MainnetBeta; export const DEFAULT_CLUSTER = Cluster.MainnetBeta;
export const DEFAULT_CUSTOM_URL = "http://localhost:8899"; export const DEFAULT_CUSTOM_URL = "http://localhost:8899";
interface State { interface State {
network: Network; cluster: Cluster;
customUrl: string; customUrl: string;
status: NetworkStatus; status: ClusterStatus;
} }
interface Connecting { interface Connecting {
status: NetworkStatus.Connecting; status: ClusterStatus.Connecting;
network: Network; cluster: Cluster;
customUrl: string; customUrl: string;
} }
interface Connected { interface Connected {
status: NetworkStatus.Connected; status: ClusterStatus.Connected;
} }
interface Failure { interface Failure {
status: NetworkStatus.Failure; status: ClusterStatus.Failure;
} }
type Action = Connected | Connecting | Failure; type Action = Connected | Connecting | Failure;
type Dispatch = (action: Action) => void; type Dispatch = (action: Action) => void;
function networkReducer(state: State, action: Action): State { function clusterReducer(state: State, action: Action): State {
switch (action.status) { switch (action.status) {
case NetworkStatus.Connected: case ClusterStatus.Connected:
case NetworkStatus.Failure: { case ClusterStatus.Failure: {
return Object.assign({}, state, { status: action.status }); return Object.assign({}, state, { status: action.status });
} }
case NetworkStatus.Connecting: { case ClusterStatus.Connecting: {
return action; return action;
} }
} }
@ -83,62 +83,62 @@ function initState(): State {
const clusterUrlParam = const clusterUrlParam =
findGetParameter("clusterUrl") || findGetParameter("networkUrl"); findGetParameter("clusterUrl") || findGetParameter("networkUrl");
let network; let cluster;
let customUrl = DEFAULT_CUSTOM_URL; let customUrl = DEFAULT_CUSTOM_URL;
switch (clusterUrlParam) { switch (clusterUrlParam) {
case MAINNET_BETA_URL: case MAINNET_BETA_URL:
network = Network.MainnetBeta; cluster = Cluster.MainnetBeta;
break; break;
case DEVNET_URL: case DEVNET_URL:
network = Network.Devnet; cluster = Cluster.Devnet;
break; break;
case TESTNET_URL: case TESTNET_URL:
network = Network.Testnet; cluster = Cluster.Testnet;
break; break;
} }
switch (clusterParam) { switch (clusterParam) {
case "mainnet-beta": case "mainnet-beta":
network = Network.MainnetBeta; cluster = Cluster.MainnetBeta;
break; break;
case "devnet": case "devnet":
network = Network.Devnet; cluster = Cluster.Devnet;
break; break;
case "testnet": case "testnet":
network = Network.Testnet; cluster = Cluster.Testnet;
break; break;
} }
if (!network) { if (!cluster) {
if (!clusterUrlParam) { if (!clusterUrlParam) {
network = DEFAULT_NETWORK; cluster = DEFAULT_CLUSTER;
} else { } else {
network = Network.Custom; cluster = Cluster.Custom;
customUrl = clusterUrlParam; customUrl = clusterUrlParam;
} }
} }
return { return {
network, cluster,
customUrl, customUrl,
status: NetworkStatus.Connecting status: ClusterStatus.Connecting
}; };
} }
const StateContext = React.createContext<State | undefined>(undefined); const StateContext = React.createContext<State | undefined>(undefined);
const DispatchContext = React.createContext<Dispatch | undefined>(undefined); const DispatchContext = React.createContext<Dispatch | undefined>(undefined);
type NetworkProviderProps = { children: React.ReactNode }; type ClusterProviderProps = { children: React.ReactNode };
export function NetworkProvider({ children }: NetworkProviderProps) { export function ClusterProvider({ children }: ClusterProviderProps) {
const [state, dispatch] = React.useReducer( const [state, dispatch] = React.useReducer(
networkReducer, clusterReducer,
undefined, undefined,
initState initState
); );
React.useEffect(() => { React.useEffect(() => {
// Connect to network immediately // Connect to cluster immediately
updateNetwork(dispatch, state.network, state.customUrl); updateCluster(dispatch, state.cluster, state.customUrl);
}, []); // eslint-disable-line react-hooks/exhaustive-deps }, []); // eslint-disable-line react-hooks/exhaustive-deps
return ( return (
@ -150,56 +150,56 @@ export function NetworkProvider({ children }: NetworkProviderProps) {
); );
} }
export function networkUrl(network: Network, customUrl: string): string { export function clusterUrl(cluster: Cluster, customUrl: string): string {
switch (network) { switch (cluster) {
case Network.Devnet: case Cluster.Devnet:
return DEVNET_URL; return DEVNET_URL;
case Network.MainnetBeta: case Cluster.MainnetBeta:
return MAINNET_BETA_URL; return MAINNET_BETA_URL;
case Network.Testnet: case Cluster.Testnet:
return TESTNET_URL; return TESTNET_URL;
case Network.Custom: case Cluster.Custom:
return customUrl; return customUrl;
} }
} }
export async function updateNetwork( export async function updateCluster(
dispatch: Dispatch, dispatch: Dispatch,
network: Network, cluster: Cluster,
customUrl: string customUrl: string
) { ) {
dispatch({ dispatch({
status: NetworkStatus.Connecting, status: ClusterStatus.Connecting,
network, cluster,
customUrl customUrl
}); });
try { try {
const connection = new Connection(networkUrl(network, customUrl)); const connection = new Connection(clusterUrl(cluster, customUrl));
await connection.getRecentBlockhash(); await connection.getRecentBlockhash();
dispatch({ status: NetworkStatus.Connected }); dispatch({ status: ClusterStatus.Connected });
} catch (error) { } catch (error) {
console.error("Failed to update network", error); console.error("Failed to update cluster", error);
dispatch({ status: NetworkStatus.Failure }); dispatch({ status: ClusterStatus.Failure });
} }
} }
export function useNetwork() { export function useCluster() {
const context = React.useContext(StateContext); const context = React.useContext(StateContext);
if (!context) { if (!context) {
throw new Error(`useNetwork must be used within a NetworkProvider`); throw new Error(`useCluster must be used within a ClusterProvider`);
} }
return { return {
...context, ...context,
url: networkUrl(context.network, context.customUrl), url: clusterUrl(context.cluster, context.customUrl),
name: networkName(context.network) name: clusterName(context.cluster)
}; };
} }
export function useNetworkDispatch() { export function useClusterDispatch() {
const context = React.useContext(DispatchContext); const context = React.useContext(DispatchContext);
if (!context) { if (!context) {
throw new Error(`useNetworkDispatch must be used within a NetworkProvider`); throw new Error(`useClusterDispatch must be used within a ClusterProvider`);
} }
return context; return context;
} }

View File

@ -1,7 +1,7 @@
import React from "react"; import React from "react";
import { TransactionSignature, Connection } from "@solana/web3.js"; import { TransactionSignature, Connection } from "@solana/web3.js";
import { findGetParameter, findPathSegment } from "../utils"; import { findGetParameter, findPathSegment } from "../utils";
import { useNetwork } from "../providers/network"; import { useCluster } from "../providers/cluster";
export enum Status { export enum Status {
Checking, Checking,
@ -124,9 +124,9 @@ type TransactionsProviderProps = { children: React.ReactNode };
export function TransactionsProvider({ children }: TransactionsProviderProps) { export function TransactionsProvider({ children }: TransactionsProviderProps) {
const [state, dispatch] = React.useReducer(reducer, undefined, initState); const [state, dispatch] = React.useReducer(reducer, undefined, initState);
const { status, url } = useNetwork(); const { status, url } = useCluster();
// Check transaction statuses on startup and whenever network updates // Check transaction statuses on startup and whenever cluster updates
React.useEffect(() => { React.useEffect(() => {
Object.values(state.transactions).forEach(tx => { Object.values(state.transactions).forEach(tx => {
checkTransactionStatus(dispatch, tx.id, tx.signature, url); checkTransactionStatus(dispatch, tx.id, tx.signature, url);