Skip to content

Commit

Permalink
Add network selector (#7)
Browse files Browse the repository at this point in the history
  • Loading branch information
jstarry authored Mar 19, 2020
1 parent c92acea commit 3c96d98
Show file tree
Hide file tree
Showing 6 changed files with 299 additions and 22 deletions.
18 changes: 17 additions & 1 deletion explorer/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@ import { NetworkProvider } from "./providers/network";
import { TransactionsProvider } from "./providers/transactions";
import NetworkStatusButton from "./components/NetworkStatusButton";
import TransactionsCard from "./components/TransactionsCard";
import NetworkModal from "./components/NetworkModal";

function App() {
const [showModal, setShowModal] = React.useState(false);
return (
<NetworkProvider>
<NetworkModal show={showModal} onClose={() => setShowModal(false)} />
<div className="main-content">
<div className="header">
<div className="container">
Expand All @@ -17,7 +20,7 @@ function App() {
<h1 className="header-title">Solana Explorer</h1>
</div>
<div className="col-auto">
<NetworkStatusButton />
<NetworkStatusButton onClick={() => setShowModal(true)} />
</div>
</div>
</div>
Expand All @@ -34,8 +37,21 @@ function App() {
</div>
</div>
</div>

<Overlay show={showModal} onClick={() => setShowModal(false)} />
</NetworkProvider>
);
}

type OverlayProps = {
show: boolean;
onClick: () => void;
};

function Overlay({ show, onClick }: OverlayProps) {
return show ? (
<div className="modal-backdrop fade show" onClick={onClick}></div>
) : null;
}

export default App;
145 changes: 145 additions & 0 deletions explorer/src/components/NetworkModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
import React from "react";
import {
useNetwork,
useNetworkDispatch,
updateNetwork,
NetworkStatus,
networkUrl,
networkName,
NETWORKS,
Network
} from "../providers/network";

type Props = {
show: boolean;
onClose: () => void;
};

function NetworkModal({ show, onClose }: Props) {
const cancelClose = React.useCallback(e => e.stopPropagation(), []);

return (
<div
className={`modal fade fixed-right ${show ? "show" : ""}`}
tabIndex={-1}
onClick={onClose}
>
<div className="modal-dialog modal-dialog-vertical">
<div className="modal-content">
<div className="modal-body" onClick={cancelClose}>
<span className="close" onClick={onClose}>
&times;
</span>

<h2 className="text-center mb-2 mt-4">Explorer Settings</h2>

<p className="text-center mb-4">
Preferences will not be saved (yet).
</p>

<hr className="mb-4" />

<h4 className="mb-1">Cluster</h4>

<p className="small text-muted mb-3">
Connect to your preferred cluster.
</p>

<NetworkToggle />
</div>
</div>
</div>
</div>
);
}

type InputProps = { activeSuffix: string; active: boolean };
function CustomNetworkInput({ activeSuffix, active }: InputProps) {
const { customUrl } = useNetwork();
const dispatch = useNetworkDispatch();
const [editing, setEditing] = React.useState(false);

const customClass = (prefix: string) =>
active ? `${prefix}-${activeSuffix}` : "";

const inputTextClass = editing ? "" : "text-muted";
return (
<div
className="btn input-group input-group-merge p-0"
onClick={() => updateNetwork(dispatch, Network.Custom, customUrl)}
>
<input
type="text"
defaultValue={customUrl}
className={`form-control form-control-prepended ${inputTextClass} ${customClass(
"border"
)}`}
onFocus={() => setEditing(true)}
onBlur={() => setEditing(false)}
onInput={e =>
updateNetwork(dispatch, Network.Custom, e.currentTarget.value)
}
/>
<div className="input-group-prepend">
<div className={`input-group-text pr-0 ${customClass("border")}`}>
<span className={customClass("text") || "text-dark"}>Custom:</span>
</div>
</div>
</div>
);
}

function NetworkToggle() {
const { status, network, customUrl } = useNetwork();
const dispatch = useNetworkDispatch();

let activeSuffix = "";
switch (status) {
case NetworkStatus.Connected:
activeSuffix = "success";
break;
case NetworkStatus.Connecting:
activeSuffix = "warning";
break;
case NetworkStatus.Failure:
activeSuffix = "danger";
break;
}

return (
<div className="btn-group-toggle d-flex flex-wrap mb-4">
{NETWORKS.map((net, index) => {
const active = net === network;
if (net === Network.Custom)
return (
<CustomNetworkInput
key={index}
activeSuffix={activeSuffix}
active={active}
/>
);

const btnClass = active
? `btn-outline-${activeSuffix}`
: "btn-white text-dark";

return (
<label
key={index}
className={`btn text-left col-12 mb-3 ${btnClass}`}
>
<input
type="radio"
checked={active}
onChange={() => updateNetwork(dispatch, net, customUrl)}
/>
{`${networkName(net)}: `}
<span className="text-muted">{networkUrl(net, customUrl)}</span>
</label>
);
})}
</div>
);
}

export default NetworkModal;
36 changes: 28 additions & 8 deletions explorer/src/components/NetworkStatusButton.tsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,47 @@
import React from "react";
import { useNetwork, NetworkStatus } from "../providers/network";
import { useNetwork, NetworkStatus, Network } from "../providers/network";

function NetworkStatusButton() {
const { status, url } = useNetwork();
function NetworkStatusButton({ onClick }: { onClick: () => void }) {
return (
<div onClick={onClick}>
<Button />
</div>
);
}

function Button() {
const { status, network, name, customUrl } = useNetwork();
const statusName =
network !== Network.Custom ? `${name} Cluster` : `${customUrl}`;

switch (status) {
case NetworkStatus.Connected:
return <span className="btn btn-white lift">{url}</span>;
return (
<span className="btn btn-outline-success lift">
<span className="fe fe-check-circle mr-2"></span>
{statusName}
</span>
);

case NetworkStatus.Connecting:
return (
<span className="btn btn-warning lift">
{"Connecting "}
<span className="btn btn-outline-warning lift">
<span
className="spinner-grow spinner-grow-sm text-dark"
className="spinner-grow spinner-grow-sm text-warning mr-2"
role="status"
aria-hidden="true"
></span>
{statusName}
</span>
);

case NetworkStatus.Failure:
return <span className="btn btn-danger lift">Disconnected</span>;
return (
<span className="btn btn-outline-danger lift">
<span className="fe fe-alert-circle mr-2"></span>
{statusName}
</span>
);
}
}

Expand Down
Loading

0 comments on commit 3c96d98

Please sign in to comment.