Remove clusterUrl url param
This commit is contained in:
parent
03f7d28aff
commit
8b95be0ee4
|
@ -1263,6 +1263,11 @@
|
||||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz",
|
||||||
"integrity": "sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw=="
|
"integrity": "sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw=="
|
||||||
},
|
},
|
||||||
|
"@react-hook/debounce": {
|
||||||
|
"version": "2.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@react-hook/debounce/-/debounce-2.0.5.tgz",
|
||||||
|
"integrity": "sha512-WrwQ1e4vx5lxxxEpGPPliMcs6oUJ8cyEb/GL8OEUPhBW4WL8YRSDW5oPnsOqIPqhHDyQMHgMipXWgj7QVmRMKA=="
|
||||||
|
},
|
||||||
"@sheerun/mutationobserver-shim": {
|
"@sheerun/mutationobserver-shim": {
|
||||||
"version": "0.3.3",
|
"version": "0.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/@sheerun/mutationobserver-shim/-/mutationobserver-shim-0.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/@sheerun/mutationobserver-shim/-/mutationobserver-shim-0.3.3.tgz",
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@react-hook/debounce": "^2.0.5",
|
||||||
"@solana/web3.js": "^0.56.0",
|
"@solana/web3.js": "^0.56.0",
|
||||||
"@testing-library/jest-dom": "^4.2.4",
|
"@testing-library/jest-dom": "^4.2.4",
|
||||||
"@testing-library/react": "^9.3.2",
|
"@testing-library/react": "^9.3.2",
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { Link, useLocation, useHistory } from "react-router-dom";
|
import { Link, useHistory, useLocation } from "react-router-dom";
|
||||||
|
import { useDebounceCallback } from "@react-hook/debounce";
|
||||||
import { Location } from "history";
|
import { Location } from "history";
|
||||||
import {
|
import {
|
||||||
useCluster,
|
useCluster,
|
||||||
|
@ -9,10 +10,12 @@ import {
|
||||||
clusterSlug,
|
clusterSlug,
|
||||||
CLUSTERS,
|
CLUSTERS,
|
||||||
Cluster,
|
Cluster,
|
||||||
useClusterModal
|
useClusterModal,
|
||||||
|
useUpdateCustomUrl
|
||||||
} from "../providers/cluster";
|
} from "../providers/cluster";
|
||||||
import { assertUnreachable } from "../utils";
|
import { assertUnreachable } from "../utils";
|
||||||
import Overlay from "./Overlay";
|
import Overlay from "./Overlay";
|
||||||
|
import { useQuery } from "utils/url";
|
||||||
|
|
||||||
function ClusterModal() {
|
function ClusterModal() {
|
||||||
const [show, setShow] = useClusterModal();
|
const [show, setShow] = useClusterModal();
|
||||||
|
@ -46,34 +49,35 @@ function ClusterModal() {
|
||||||
type InputProps = { activeSuffix: string; active: boolean };
|
type InputProps = { activeSuffix: string; active: boolean };
|
||||||
function CustomClusterInput({ activeSuffix, active }: InputProps) {
|
function CustomClusterInput({ activeSuffix, active }: InputProps) {
|
||||||
const { customUrl } = useCluster();
|
const { customUrl } = useCluster();
|
||||||
|
const updateCustomUrl = useUpdateCustomUrl();
|
||||||
const [editing, setEditing] = React.useState(false);
|
const [editing, setEditing] = React.useState(false);
|
||||||
|
const query = useQuery();
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
|
|
||||||
const customClass = (prefix: string) =>
|
const customClass = (prefix: string) =>
|
||||||
active ? `${prefix}-${activeSuffix}` : "";
|
active ? `${prefix}-${activeSuffix}` : "";
|
||||||
|
|
||||||
const clusterLocation = (location: Location, url: string) => {
|
const clusterLocation = (location: Location) => {
|
||||||
const params = new URLSearchParams(location.search);
|
if (customUrl.length > 0) query.set("cluster", "custom");
|
||||||
params.set("clusterUrl", url);
|
|
||||||
params.delete("cluster");
|
|
||||||
return {
|
return {
|
||||||
...location,
|
...location,
|
||||||
search: params.toString()
|
search: query.toString()
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateCustomUrl = React.useCallback(
|
const onUrlInput = useDebounceCallback((url: string) => {
|
||||||
(url: string) => {
|
updateCustomUrl(url);
|
||||||
history.push(clusterLocation(location, url));
|
if (url.length > 0) {
|
||||||
},
|
query.set("cluster", "custom");
|
||||||
[history, location]
|
history.push({ ...location, search: query.toString() });
|
||||||
);
|
}
|
||||||
|
}, 500);
|
||||||
|
|
||||||
const inputTextClass = editing ? "" : "text-muted";
|
const inputTextClass = editing ? "" : "text-muted";
|
||||||
return (
|
return (
|
||||||
<Link
|
<Link
|
||||||
to={location => clusterLocation(location, customUrl)}
|
to={location => clusterLocation(location)}
|
||||||
className="btn input-group input-group-merge p-0"
|
className="btn input-group input-group-merge p-0"
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
|
@ -84,7 +88,7 @@ function CustomClusterInput({ activeSuffix, active }: InputProps) {
|
||||||
)}`}
|
)}`}
|
||||||
onFocus={() => setEditing(true)}
|
onFocus={() => setEditing(true)}
|
||||||
onBlur={() => setEditing(false)}
|
onBlur={() => setEditing(false)}
|
||||||
onInput={e => updateCustomUrl(e.currentTarget.value)}
|
onInput={e => onUrlInput(e.currentTarget.value)}
|
||||||
/>
|
/>
|
||||||
<div className="input-group-prepend">
|
<div className="input-group-prepend">
|
||||||
<div className={`input-group-text pr-0 ${customClass("border")}`}>
|
<div className={`input-group-text pr-0 ${customClass("border")}`}>
|
||||||
|
@ -133,14 +137,10 @@ function ClusterToggle() {
|
||||||
const clusterLocation = (location: Location) => {
|
const clusterLocation = (location: Location) => {
|
||||||
const params = new URLSearchParams(location.search);
|
const params = new URLSearchParams(location.search);
|
||||||
const slug = clusterSlug(net);
|
const slug = clusterSlug(net);
|
||||||
if (slug && slug !== "mainnet-beta") {
|
if (slug !== "mainnet-beta") {
|
||||||
params.set("cluster", slug);
|
params.set("cluster", slug);
|
||||||
params.delete("clusterUrl");
|
|
||||||
} else {
|
} else {
|
||||||
params.delete("cluster");
|
params.delete("cluster");
|
||||||
if (slug === "mainnet-beta") {
|
|
||||||
params.delete("clusterUrl");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
...location,
|
...location,
|
||||||
|
|
|
@ -3,10 +3,9 @@ import {
|
||||||
useFetchTransactionStatus,
|
useFetchTransactionStatus,
|
||||||
useTransactionStatus,
|
useTransactionStatus,
|
||||||
useTransactionDetails,
|
useTransactionDetails,
|
||||||
useDetailsDispatch,
|
|
||||||
FetchStatus
|
FetchStatus
|
||||||
} from "../providers/transactions";
|
} from "../providers/transactions";
|
||||||
import { fetchDetails } from "providers/transactions/details";
|
import { useFetchTransactionDetails } from "providers/transactions/details";
|
||||||
import { useCluster, useClusterModal } from "providers/cluster";
|
import { useCluster, useClusterModal } from "providers/cluster";
|
||||||
import {
|
import {
|
||||||
TransactionSignature,
|
TransactionSignature,
|
||||||
|
@ -206,12 +205,11 @@ function StatusCard({ signature }: Props) {
|
||||||
|
|
||||||
function AccountsCard({ signature }: Props) {
|
function AccountsCard({ signature }: Props) {
|
||||||
const details = useTransactionDetails(signature);
|
const details = useTransactionDetails(signature);
|
||||||
const dispatch = useDetailsDispatch();
|
|
||||||
const { url } = useCluster();
|
|
||||||
|
|
||||||
const fetchStatus = useFetchTransactionStatus();
|
const fetchStatus = useFetchTransactionStatus();
|
||||||
|
const fetchDetails = useFetchTransactionDetails();
|
||||||
const refreshStatus = () => fetchStatus(signature);
|
const refreshStatus = () => fetchStatus(signature);
|
||||||
const refreshDetails = () => fetchDetails(dispatch, signature, url);
|
const refreshDetails = () => fetchDetails(signature);
|
||||||
const transaction = details?.transaction?.transaction;
|
const transaction = details?.transaction?.transaction;
|
||||||
const message = React.useMemo(() => {
|
const message = React.useMemo(() => {
|
||||||
return transaction?.compileMessage();
|
return transaction?.compileMessage();
|
||||||
|
@ -308,9 +306,8 @@ function AccountsCard({ signature }: Props) {
|
||||||
function InstructionsSection({ signature }: Props) {
|
function InstructionsSection({ signature }: Props) {
|
||||||
const status = useTransactionStatus(signature);
|
const status = useTransactionStatus(signature);
|
||||||
const details = useTransactionDetails(signature);
|
const details = useTransactionDetails(signature);
|
||||||
const dispatch = useDetailsDispatch();
|
const fetchDetails = useFetchTransactionDetails();
|
||||||
const { url } = useCluster();
|
const refreshDetails = () => fetchDetails(signature);
|
||||||
const refreshDetails = () => fetchDetails(dispatch, signature, url);
|
|
||||||
|
|
||||||
if (!status || !status.info || !details || !details.transaction) return null;
|
if (!status || !status.info || !details || !details.transaction) return null;
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { clusterApiUrl, Connection } from "@solana/web3.js";
|
import { clusterApiUrl, Connection } from "@solana/web3.js";
|
||||||
import { useQuery } from "../utils/url";
|
import { useQuery } from "../utils/url";
|
||||||
|
import { useHistory, useLocation } from "react-router-dom";
|
||||||
|
|
||||||
export enum ClusterStatus {
|
export enum ClusterStatus {
|
||||||
Connected,
|
Connected,
|
||||||
|
@ -22,7 +23,7 @@ export const CLUSTERS = [
|
||||||
Cluster.Custom
|
Cluster.Custom
|
||||||
];
|
];
|
||||||
|
|
||||||
export function clusterSlug(cluster: Cluster): string | undefined {
|
export function clusterSlug(cluster: Cluster): string {
|
||||||
switch (cluster) {
|
switch (cluster) {
|
||||||
case Cluster.MainnetBeta:
|
case Cluster.MainnetBeta:
|
||||||
return "mainnet-beta";
|
return "mainnet-beta";
|
||||||
|
@ -31,7 +32,7 @@ export function clusterSlug(cluster: Cluster): string | undefined {
|
||||||
case Cluster.Devnet:
|
case Cluster.Devnet:
|
||||||
return "devnet";
|
return "devnet";
|
||||||
case Cluster.Custom:
|
case Cluster.Custom:
|
||||||
return undefined;
|
return "custom";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,8 +53,20 @@ export const MAINNET_BETA_URL = clusterApiUrl("mainnet-beta");
|
||||||
export const TESTNET_URL = clusterApiUrl("testnet");
|
export const TESTNET_URL = clusterApiUrl("testnet");
|
||||||
export const DEVNET_URL = clusterApiUrl("devnet");
|
export const DEVNET_URL = clusterApiUrl("devnet");
|
||||||
|
|
||||||
|
export function clusterUrl(cluster: Cluster, customUrl: string): string {
|
||||||
|
switch (cluster) {
|
||||||
|
case Cluster.Devnet:
|
||||||
|
return DEVNET_URL;
|
||||||
|
case Cluster.MainnetBeta:
|
||||||
|
return MAINNET_BETA_URL;
|
||||||
|
case Cluster.Testnet:
|
||||||
|
return TESTNET_URL;
|
||||||
|
case Cluster.Custom:
|
||||||
|
return customUrl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export const DEFAULT_CLUSTER = Cluster.MainnetBeta;
|
export const DEFAULT_CLUSTER = Cluster.MainnetBeta;
|
||||||
export const DEFAULT_CUSTOM_URL = "http://localhost:8899";
|
|
||||||
|
|
||||||
interface State {
|
interface State {
|
||||||
cluster: Cluster;
|
cluster: Cluster;
|
||||||
|
@ -88,51 +101,19 @@ function clusterReducer(state: State, action: Action): State {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseQuery(
|
function parseQuery(query: URLSearchParams): Cluster {
|
||||||
query: URLSearchParams
|
|
||||||
): { cluster: Cluster; customUrl: string } {
|
|
||||||
const clusterParam = query.get("cluster");
|
const clusterParam = query.get("cluster");
|
||||||
const clusterUrlParam = query.get("clusterUrl");
|
|
||||||
|
|
||||||
let cluster;
|
|
||||||
let customUrl = DEFAULT_CUSTOM_URL;
|
|
||||||
switch (clusterUrlParam) {
|
|
||||||
case MAINNET_BETA_URL:
|
|
||||||
cluster = Cluster.MainnetBeta;
|
|
||||||
break;
|
|
||||||
case DEVNET_URL:
|
|
||||||
cluster = Cluster.Devnet;
|
|
||||||
break;
|
|
||||||
case TESTNET_URL:
|
|
||||||
cluster = Cluster.Testnet;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (clusterParam) {
|
switch (clusterParam) {
|
||||||
case "mainnet-beta":
|
case "custom":
|
||||||
cluster = Cluster.MainnetBeta;
|
return Cluster.Custom;
|
||||||
break;
|
|
||||||
case "devnet":
|
case "devnet":
|
||||||
cluster = Cluster.Devnet;
|
return Cluster.Devnet;
|
||||||
break;
|
|
||||||
case "testnet":
|
case "testnet":
|
||||||
cluster = Cluster.Testnet;
|
return Cluster.Testnet;
|
||||||
break;
|
case "mainnet-beta":
|
||||||
|
default:
|
||||||
|
return Cluster.MainnetBeta;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!cluster) {
|
|
||||||
if (!clusterUrlParam) {
|
|
||||||
cluster = DEFAULT_CLUSTER;
|
|
||||||
} else {
|
|
||||||
cluster = Cluster.Custom;
|
|
||||||
customUrl = clusterUrlParam;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
cluster,
|
|
||||||
customUrl
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type SetShowModal = React.Dispatch<React.SetStateAction<boolean>>;
|
type SetShowModal = React.Dispatch<React.SetStateAction<boolean>>;
|
||||||
|
@ -146,16 +127,28 @@ type ClusterProviderProps = { children: React.ReactNode };
|
||||||
export function ClusterProvider({ children }: ClusterProviderProps) {
|
export function ClusterProvider({ children }: ClusterProviderProps) {
|
||||||
const [state, dispatch] = React.useReducer(clusterReducer, {
|
const [state, dispatch] = React.useReducer(clusterReducer, {
|
||||||
cluster: DEFAULT_CLUSTER,
|
cluster: DEFAULT_CLUSTER,
|
||||||
customUrl: DEFAULT_CUSTOM_URL,
|
customUrl: "",
|
||||||
status: ClusterStatus.Connecting
|
status: ClusterStatus.Connecting
|
||||||
});
|
});
|
||||||
const [showModal, setShowModal] = React.useState(false);
|
const [showModal, setShowModal] = React.useState(false);
|
||||||
const { cluster, customUrl } = parseQuery(useQuery());
|
const query = useQuery();
|
||||||
|
const cluster = parseQuery(query);
|
||||||
|
const history = useHistory();
|
||||||
|
const location = useLocation();
|
||||||
|
|
||||||
// Reconnect to cluster when it changes
|
// Reconnect to cluster when params change
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
updateCluster(dispatch, cluster, customUrl);
|
if (cluster === Cluster.Custom) {
|
||||||
}, [cluster, customUrl]);
|
// Remove cluster param if custom url has not been set
|
||||||
|
if (state.customUrl.length === 0) {
|
||||||
|
query.delete("cluster");
|
||||||
|
history.push({ ...location, search: query.toString() });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateCluster(dispatch, cluster, state.customUrl);
|
||||||
|
}, [cluster, state.customUrl]); // eslint-disable-line react-hooks/exhaustive-deps
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StateContext.Provider value={state}>
|
<StateContext.Provider value={state}>
|
||||||
|
@ -168,19 +161,6 @@ export function ClusterProvider({ children }: ClusterProviderProps) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function clusterUrl(cluster: Cluster, customUrl: string): string {
|
|
||||||
switch (cluster) {
|
|
||||||
case Cluster.Devnet:
|
|
||||||
return DEVNET_URL;
|
|
||||||
case Cluster.MainnetBeta:
|
|
||||||
return MAINNET_BETA_URL;
|
|
||||||
case Cluster.Testnet:
|
|
||||||
return TESTNET_URL;
|
|
||||||
case Cluster.Custom:
|
|
||||||
return customUrl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function updateCluster(
|
async function updateCluster(
|
||||||
dispatch: Dispatch,
|
dispatch: Dispatch,
|
||||||
cluster: Cluster,
|
cluster: Cluster,
|
||||||
|
@ -207,6 +187,17 @@ async function updateCluster(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function useUpdateCustomUrl() {
|
||||||
|
const dispatch = React.useContext(DispatchContext);
|
||||||
|
if (!dispatch) {
|
||||||
|
throw new Error(`useUpdateCustomUrl must be used within a ClusterProvider`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (customUrl: string) => {
|
||||||
|
updateCluster(dispatch, Cluster.Custom, customUrl);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export function useCluster() {
|
export function useCluster() {
|
||||||
const context = React.useContext(StateContext);
|
const context = React.useContext(StateContext);
|
||||||
if (!context) {
|
if (!context) {
|
||||||
|
|
|
@ -128,7 +128,7 @@ export function DetailsProvider({ children }: DetailsProviderProps) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function fetchDetails(
|
async function fetchDetails(
|
||||||
dispatch: Dispatch,
|
dispatch: Dispatch,
|
||||||
signature: TransactionSignature,
|
signature: TransactionSignature,
|
||||||
url: string
|
url: string
|
||||||
|
@ -151,3 +151,17 @@ export async function fetchDetails(
|
||||||
}
|
}
|
||||||
dispatch({ type: ActionType.Update, fetchStatus, signature, transaction });
|
dispatch({ type: ActionType.Update, fetchStatus, signature, transaction });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function useFetchTransactionDetails() {
|
||||||
|
const dispatch = React.useContext(DispatchContext);
|
||||||
|
if (!dispatch) {
|
||||||
|
throw new Error(
|
||||||
|
`useFetchTransactionDetails must be used within a TransactionsProvider`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { url } = useCluster();
|
||||||
|
return (signature: TransactionSignature) => {
|
||||||
|
url && fetchDetails(dispatch, signature, url);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
@ -12,8 +12,7 @@ import { useQuery } from "../../utils/url";
|
||||||
import { useCluster, Cluster, ClusterStatus } from "../cluster";
|
import { useCluster, Cluster, ClusterStatus } from "../cluster";
|
||||||
import {
|
import {
|
||||||
DetailsProvider,
|
DetailsProvider,
|
||||||
StateContext as DetailsStateContext,
|
StateContext as DetailsStateContext
|
||||||
DispatchContext as DetailsDispatchContext
|
|
||||||
} from "./details";
|
} from "./details";
|
||||||
import base58 from "bs58";
|
import base58 from "bs58";
|
||||||
import { useFetchAccountInfo } from "../accounts";
|
import { useFetchAccountInfo } from "../accounts";
|
||||||
|
@ -308,16 +307,6 @@ export function useTransactionDetails(signature: TransactionSignature) {
|
||||||
return context[signature];
|
return context[signature];
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useDetailsDispatch() {
|
|
||||||
const context = React.useContext(DetailsDispatchContext);
|
|
||||||
if (!context) {
|
|
||||||
throw new Error(
|
|
||||||
`useDetailsDispatch must be used within a TransactionsProvider`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return context;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function useFetchTransactionStatus() {
|
export function useFetchTransactionStatus() {
|
||||||
const dispatch = React.useContext(DispatchContext);
|
const dispatch = React.useContext(DispatchContext);
|
||||||
if (!dispatch) {
|
if (!dispatch) {
|
||||||
|
|
Loading…
Reference in New Issue