From f905f665eb8707a967faf6a99968f27b9c3cedbf Mon Sep 17 00:00:00 2001 From: shanejonas Date: Thu, 12 Aug 2021 13:00:37 -0700 Subject: [PATCH] fix: url override --- package-lock.json | 38 +++++++- src/App.tsx | 178 +++++++++++++++++++++++++---------- src/hooks/useEthRPC.ts | 17 ++-- src/stores/useEthRPCStore.ts | 7 +- 4 files changed, 180 insertions(+), 60 deletions(-) diff --git a/package-lock.json b/package-lock.json index cd99735..3c4c8c1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1324,7 +1324,7 @@ } }, "@etclabscore/ethereum-json-rpc-specification": { - "version": "git+https://github.com/etclabscore/ethereum-json-rpc-specification.git#97178e7dc3b417318e2977171254f6e21d080076", + "version": "git+https://github.com/etclabscore/ethereum-json-rpc-specification.git#94c09745a4920208399d50238cfbc4605ace4ae7", "from": "git+https://github.com/etclabscore/ethereum-json-rpc-specification.git" }, "@hapi/address": { @@ -4501,6 +4501,16 @@ "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", "dev": true }, + "bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "dev": true, + "optional": true, + "requires": { + "file-uri-to-path": "1.0.0" + } + }, "blakejs": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/blakejs/-/blakejs-1.1.0.tgz", @@ -8061,6 +8071,13 @@ "schema-utils": "^2.5.0" } }, + "file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "dev": true, + "optional": true + }, "filename-reserved-regex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-1.0.0.tgz", @@ -10686,7 +10703,11 @@ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", "dev": true, - "optional": true + "optional": true, + "requires": { + "bindings": "^1.5.0", + "nan": "^2.12.1" + } }, "is-number": { "version": "3.0.0", @@ -12412,6 +12433,13 @@ "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", "dev": true }, + "nan": { + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz", + "integrity": "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==", + "dev": true, + "optional": true + }, "nanomatch": { "version": "1.2.13", "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", @@ -22724,7 +22752,11 @@ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", "dev": true, - "optional": true + "optional": true, + "requires": { + "bindings": "^1.5.0", + "nan": "^2.12.1" + } }, "glob-parent": { "version": "3.1.0", diff --git a/src/App.tsx b/src/App.tsx index 122c0d8..e33910c 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,7 +1,23 @@ -import { AppBar, CssBaseline, Toolbar, Typography, IconButton, Grid, InputBase, Tooltip, CircularProgress } from "@material-ui/core"; +import { + AppBar, + CssBaseline, + Toolbar, + Typography, + IconButton, + Grid, + InputBase, + Tooltip, + CircularProgress, +} from "@material-ui/core"; import { ThemeProvider } from "@material-ui/styles"; import Link from "@material-ui/core/Link"; -import React, { Dispatch, ChangeEvent, KeyboardEvent, useState, useEffect } from "react"; +import React, { + Dispatch, + ChangeEvent, + KeyboardEvent, + useState, + useEffect, +} from "react"; import { Link as RouterLink, Router, Route, Switch } from "react-router-dom"; import useDarkMode from "use-dark-mode"; import "./App.css"; @@ -22,7 +38,11 @@ import { useTranslation } from "react-i18next"; import LanguageMenu from "./containers/LanguageMenu"; import { createBrowserHistory } from "history"; import ChainDropdown from "./components/ChainDropdown/ChainDropdown"; -import { StringParam, QueryParamProvider, useQueryParams } from "use-query-params"; +import { + StringParam, + QueryParamProvider, + useQueryParams, +} from "use-query-params"; import { createPreserveQueryHistory } from "./helpers/createPreserveHistory"; import BlockRawContainer from "./containers/BlockRawContainer"; import TransactionRawContainer from "./containers/TransactionRawContainer"; @@ -32,8 +52,12 @@ import { IChain as Chain } from "./models/chain"; import useChainListStore from "./stores/useChainListStore"; import useEthRPCStore from "./stores/useEthRPCStore"; import AddChain from "./components/AddChain/AddChain"; +import { NetworkWifi } from "@material-ui/icons"; -const history = createPreserveQueryHistory(createBrowserHistory, ["network", "rpcUrl"])(); +const history = createPreserveQueryHistory(createBrowserHistory, [ + "network", + "rpcUrl", +])(); function App(props: any) { const { t } = useTranslation(); @@ -45,13 +69,23 @@ function App(props: any) { const [chains, setChains] = useChainListStore<[Chain[], Dispatch]>(); const [ethRPC, setEthRPCChain] = useEthRPCStore(); - const [addChainDialogIsOpen, setAddChainDialogIsOpen] = useState(false); + const [addChainDialogIsOpen, setAddChainDialogIsOpen] = + useState(false); // default the selectedChain once chain list loads useEffect(() => { - if (selectedChain !== undefined) { return; } - if (chains === undefined) { return; } - if (chains.length === 0) { return; } + if (selectedChain !== undefined) { + return; + } + if (chains === undefined) { + return; + } + if (chains.length === 0) { + return; + } + if (query.rpcUrl) { + return; + } setSelectedChain(chains[0]); @@ -80,12 +114,14 @@ function App(props: any) { } if (chains && query.network) { - const foundChain = chains.find((chain: Chain) => chain.name === query.network); + const foundChain = chains.find( + (chain: Chain) => chain.name === query.network + ); setSelectedChain(foundChain); } else { setSelectedChain(chains[0]); } - // eslint-disable-next-line react-hooks/exhaustive-deps + // eslint-disable-next-line react-hooks/exhaustive-deps }, [chains, query.network]); // keeps the window.location in sync with selected network @@ -102,7 +138,7 @@ function App(props: any) { search: `?network=${name}`, }); } - // eslint-disable-next-line react-hooks/exhaustive-deps + // eslint-disable-next-line react-hooks/exhaustive-deps }, [selectedChain, setQuery]); // keep selected chain in sync with the current ethrpc instance @@ -118,12 +154,16 @@ function App(props: any) { } }, [ethRPC]); - useInterval(() => { - if (ethRPC) { - ethRPC.stopBatch(); - ethRPC.startBatch(); - } - }, 100, true); + useInterval( + () => { + if (ethRPC) { + ethRPC.stopBatch(); + ethRPC.startBatch(); + } + }, + 100, + true + ); const isAddress = (q: string): boolean => { const re = new RegExp(ETHJSONSpec.components.schemas.Address.pattern); @@ -141,7 +181,9 @@ function App(props: any) { }; const handleSearch = async (qry: string | undefined) => { - if (qry === undefined) { return; } + if (qry === undefined) { + return; + } const q = qry.trim(); if (isAddress(q)) { history.push(`/address/${q}`); @@ -169,7 +211,10 @@ function App(props: any) { } } if (isBlockNumber(q)) { - const block = await ethRPC.eth_getBlockByNumber(`0x${parseInt(q, 10).toString(16)}`, false); + const block = await ethRPC.eth_getBlockByNumber( + `0x${parseInt(q, 10).toString(16)}`, + false + ); if (block) { history.push(`/block/${block.hash}`); } @@ -195,14 +240,26 @@ function App(props: any) { - + ( + component={({ + className, + children, + }: { + children: any; + className: string; + }) => ( {children} - )}> + )} + > ) => { - if (event.keyCode === 13) { - handleSearch(search); - } + placeholder={t( + "Enter an Address, Transaction Hash or Block Number" + )} + onKeyDown={(event: KeyboardEvent) => { + if (event.keyCode === 13) { + handleSearch(search); } - } - onChange={ - (event: ChangeEvent) => { - if (event.target.value) { - const {value} = event.target; - setSearch(value as any); - } + }} + onChange={(event: ChangeEvent) => { + if (event.target.value) { + const { value } = event.target; + setSearch(value as any); } - } + }} fullWidth style={{ background: "rgba(0,0,0,0.1)", @@ -248,11 +303,24 @@ function App(props: any) { /> - {selectedChain ? - : } + {selectedChain ? ( + + ) : ( + <> + {query && query.rpcUrl && ( + + + + + + )} + {!query.rpcUrl && } + + )} @@ -261,9 +329,13 @@ function App(props: any) { - window.open("https://playground.open-rpc.org/?schemaUrl=https://raw.githubusercontent.com/etclabscore/ethereum-json-rpc-specification/master/openrpc.json") //tslint:disable-line - }> + onClick={ + () => + window.open( + "https://playground.open-rpc.org/?schemaUrl=https://raw.githubusercontent.com/etclabscore/ethereum-json-rpc-specification/master/openrpc.json" + ) //tslint:disable-line + } + > @@ -271,7 +343,8 @@ function App(props: any) { window.open("https://github.com/xops/expedition") - }> + } + > @@ -294,20 +367,27 @@ function App(props: any) { - + - + - - + + ); } diff --git a/src/hooks/useEthRPC.ts b/src/hooks/useEthRPC.ts index 4b392d7..01aa13a 100644 --- a/src/hooks/useEthRPC.ts +++ b/src/hooks/useEthRPC.ts @@ -1,15 +1,16 @@ import ERPC from "@etclabscore/ethereum-json-rpc"; -import React, { Dispatch } from "react"; +import React, { Dispatch, useState, useEffect } from "react"; import { IChain as Chain } from "../models/chain"; -function useEthRPC(): [ERPC, Dispatch] { +function useEthRPC(queryUrlOverride?: string): [ERPC, Dispatch] { const [erpc, setErpc] = React.useState(); const [selectedChain, setSelectedChain] = React.useState(); + const [urlOverride] = useState(queryUrlOverride || process.env.REACT_APP_ETH_RPC_URL); - React.useEffect(() => { - if (selectedChain === undefined) { return; } + useEffect(() => { + if (selectedChain === undefined && !urlOverride) { return; } - const rpcUrl = selectedChain.rpc.reduce((curr, toCheck) => { + const rpcUrl = selectedChain?.rpc.reduce((curr, toCheck) => { if (curr !== selectedChain.rpc[0]) { return curr; } if (toCheck.indexOf("${") !== -1) { return curr; } return toCheck; @@ -17,10 +18,12 @@ function useEthRPC(): [ERPC, Dispatch] { const runAsync = async () => { let parsedUrl; + const newUrl = urlOverride || rpcUrl; + if (!newUrl) { return; } try { - parsedUrl = new URL(rpcUrl); + parsedUrl = new URL(newUrl); } catch (e) { - alert("invalid rpc url " + rpcUrl); + alert("invalid rpc url " + newUrl); return; } let rpc; diff --git a/src/stores/useEthRPCStore.ts b/src/stores/useEthRPCStore.ts index 6ad8486..adfa251 100644 --- a/src/stores/useEthRPCStore.ts +++ b/src/stores/useEthRPCStore.ts @@ -1,4 +1,9 @@ import { createStore } from "reusable"; import useEthRPC from "../hooks/useEthRPC"; +import { useQueryParam, StringParam } from "use-query-params"; -export default createStore(() => useEthRPC()); +export default createStore(() => { + const [rpcUrlQuery] = useQueryParam("rpcUrl", StringParam); + + return useEthRPC(rpcUrlQuery); +});