diff --git a/.env.development b/.env.development deleted file mode 100644 index 13223e44..00000000 --- a/.env.development +++ /dev/null @@ -1 +0,0 @@ -REACT_APP_BASE_URL='edge.testnet.solana.com' diff --git a/.env.production b/.env.production deleted file mode 100644 index 2180b246..00000000 --- a/.env.production +++ /dev/null @@ -1 +0,0 @@ -REACT_APP_BASE_URL='help.i.am.broken' diff --git a/src/AppV2.js b/src/AppV2.js index 401ae04e..946450c6 100644 --- a/src/AppV2.js +++ b/src/AppV2.js @@ -4,7 +4,6 @@ import React, {lazy, Suspense} from 'react'; import {hot} from 'react-hot-loader/root'; import {Route, Switch} from 'react-router-dom'; import Header from 'v2/components/Header'; -import NavBar from 'v2/components/NavBar'; import Footer from 'v2/components/Footer'; import theme from 'v2/theme'; import socket from 'v2/stores/socket'; @@ -25,13 +24,16 @@ const useStyles = makeStyles(theme => ({ overflow: 'hidden', }, content: { - flexGrow: 1, marginLeft: 50, + minWidth: '1px', padding: '50px 24px 0 24px', - maxWidth: '100%', + maxWidth: 'calc(100% - 50px)', + width: '100%', [theme.breakpoints.down('sm')]: { marginLeft: 0, padding: 0, + paddingTop: 80, + maxWidth: '100%', }, }, toolbar: { @@ -50,7 +52,6 @@ const App = () => {
-
Loading...
}> diff --git a/src/index.js b/src/index.js index f2be12a0..93044d29 100755 --- a/src/index.js +++ b/src/index.js @@ -12,7 +12,7 @@ async function main() { await EndpointConfig.load(); ReactDOM.render( - {window.location.pathname.includes('rc') ? ( + {window.location.pathname.startsWith('/rc') ? ( Loading...
}> diff --git a/src/v2/api/index.js b/src/v2/api/index.js index 902163f2..fccb8cef 100644 --- a/src/v2/api/index.js +++ b/src/v2/api/index.js @@ -2,10 +2,11 @@ import axios from 'axios'; import humps from 'humps'; -import {BLOCK_EXPLORER_API_BASE} from 'v2/const'; + +import {getApiUrl} from '../../EndpointConfig'; const api = axios.create({ - baseURL: BLOCK_EXPLORER_API_BASE, + baseURL: getApiUrl(), }); api.defaults.transformResponse = [ @@ -24,4 +25,8 @@ api.defaults.transformRequest = [ ...axios.defaults.transformRequest, ]; +export function updateBaseUrl() { + api.defaults.baseURL = getApiUrl(); +} + export default api; diff --git a/src/v2/api/stats.js b/src/v2/api/stats.js index 4616531c..a0e08245 100644 --- a/src/v2/api/stats.js +++ b/src/v2/api/stats.js @@ -7,3 +7,7 @@ export function getStats() { export function getTxnStats() { return api('/txn-stats'); } + +export function getClusterInfo() { + return api('/cluster-info'); +} diff --git a/src/v2/components/Footer/styles.js b/src/v2/components/Footer/styles.js index 3c7f585b..c9241683 100644 --- a/src/v2/components/Footer/styles.js +++ b/src/v2/components/Footer/styles.js @@ -38,9 +38,11 @@ export default makeStyles(theme => ({ bg: { position: 'absolute', left: -160, - [theme.breakpoints.down('md')]: { + [theme.breakpoints.down('sm')]: { position: 'static', width: '100%', + height: 70, + objectFit: 'cover', }, }, })); diff --git a/src/v2/components/Header/index.jsx b/src/v2/components/Header/index.jsx index bb2e1001..c7ac28b5 100644 --- a/src/v2/components/Header/index.jsx +++ b/src/v2/components/Header/index.jsx @@ -1,20 +1,22 @@ // @flow -import Select from '@material-ui/core/Select'; -import React from 'react'; +import {Select, AppBar, Toolbar, IconButton} from '@material-ui/core'; +import MenuIcon from '@material-ui/icons/Menu'; +import React, {useState} from 'react'; import {observer} from 'mobx-react-lite'; -import AppBar from '@material-ui/core/AppBar'; -import Toolbar from '@material-ui/core/Toolbar'; +import {map} from 'lodash/fp'; import Logo from 'v2/components/UI/Logo'; import Search from 'v2/components/Search'; import socketActions from 'v2/stores/socket'; -import {map} from 'lodash/fp'; +import {updateBaseUrl} from 'v2/api'; import * as EndpointConfig from '../../../EndpointConfig'; +import NavBar from '../NavBar'; import {ReactComponent as LiveIcon} from './assets/liveIcon.svg'; import useStyles from './styles'; const Header = () => { + const [isDrawerOpen, setDrawerOpen] = useState(false); const {endpointName, updateEndpointName} = socketActions; const classes = useStyles(); const onSearch = () => {}; @@ -23,6 +25,7 @@ const Header = () => { EndpointConfig.setEndpointName(endpointName); updateEndpointName(endpointName); socketActions.init(); + updateBaseUrl(); }; const endPointsList = EndpointConfig.getEndpoints(); const renderEndpointOption = endpoint => ( @@ -30,24 +33,50 @@ const Header = () => { {endpoint} ); + + const toggleDrawer = open => event => { + if ( + event && + event.type === 'keydown' && + (event.key === 'Tab' || event.key === 'Shift') + ) { + return; + } + setDrawerOpen(open); + }; + return ( - - - -
- -
-
-

Real-time updated:

-
- Every 5 sec + <> + + + +
+ +
+
+

Real-time updated:

+
+ Every 5 sec +
-
- - - + + + + + + + + ); }; diff --git a/src/v2/components/Header/styles.js b/src/v2/components/Header/styles.js index 625dc1b6..c9e08db0 100644 --- a/src/v2/components/Header/styles.js +++ b/src/v2/components/Header/styles.js @@ -4,25 +4,41 @@ import getColor from 'v2/utils/getColor'; export default makeStyles(theme => ({ root: { - padding: '18px 68px 18px 28px', + padding: '18px 28px', borderBottom: `1px solid ${fade(theme.palette.primary.grey3, 0.5)}`, zIndex: theme.zIndex.drawer + 1, + [theme.breakpoints.down('sm')]: { + padding: '18px 0', + }, + }, + inner: { + [theme.breakpoints.down('sm')]: { + flexWrap: 'wrap', + }, }, search: { - marginLeft: 66, + marginLeft: 25, marginRight: 'auto', width: '100%', maxWidth: 713, + [theme.breakpoints.down('sm')]: { + order: 1, + marginLeft: 0, + maxWidth: '100%', + }, }, realTime: { display: 'flex', alignItems: 'center', whiteSpace: 'nowrap', - marginLeft: 66, + marginLeft: 25, textTransform: 'uppercase', fontSize: 12, letterSpacing: 2.5, marginRight: 25, + [theme.breakpoints.down('sm')]: { + display: 'none', + }, '& div': { background: 'transparent', border: `1px solid ${getColor('main')(theme)}`, @@ -38,4 +54,17 @@ export default makeStyles(theme => ({ }, }, }, + networkSelect: { + minWidth: 125, + [theme.breakpoints.down('sm')]: { + display: 'none', + }, + }, + menuButton: { + display: 'none', + marginLeft: 'auto', + [theme.breakpoints.down('sm')]: { + display: 'block', + }, + }, })); diff --git a/src/v2/components/NavBar/index.jsx b/src/v2/components/NavBar/index.jsx index 9fed8e01..c0da224c 100644 --- a/src/v2/components/NavBar/index.jsx +++ b/src/v2/components/NavBar/index.jsx @@ -1,8 +1,16 @@ // @flow -import {Drawer, List, ListItem, ListItemIcon} from '@material-ui/core'; +import { + SwipeableDrawer, + List, + ListItem, + ListItemIcon, + ListItemText, + IconButton, +} from '@material-ui/core'; import useMediaQuery from '@material-ui/core/useMediaQuery'; import {useTheme} from '@material-ui/core/styles'; +import CloseIcon from '@material-ui/icons/Close'; import {RouterHistory, withRouter} from 'react-router-dom'; import React from 'react'; import {map, propEq, eq} from 'lodash/fp'; @@ -29,13 +37,17 @@ const icons = { const NavBar = ({ location, history, + isOpen, + toggleDrawer, }: { location: Location, history: RouterHistory, + isOpen: boolean, + toggleDrawer: (val: boolean) => void, }) => { const classes = useStyles(); const theme = useTheme(); - const showDriver = useMediaQuery(theme.breakpoints.up('md')); + const showDrawer = useMediaQuery(theme.breakpoints.up('md')); const routes = [ 'dashboard', 'transactions', @@ -49,9 +61,14 @@ const NavBar = ({ const Icon = icons[link]; const isDashboard = eq('dashboard', link); const selected = - propEq('pathname', `/${link}`)(location) || - (propEq('pathname', '/')(location) && isDashboard); - const changeRoute = () => history.push(`/rc/${isDashboard ? '' : link}`); + propEq('pathname', `/rc/${link}`)(location) || + (propEq('pathname', '/rc/')(location) && isDashboard); + const changeRoute = () => { + if (selected) { + return; + } + history.push(`/rc/${isDashboard ? '' : link}`); + }; return ( + ); }; + return (
-
+ + + {map(renderLink)(routes)} - +
); }; diff --git a/src/v2/components/NavBar/styles.js b/src/v2/components/NavBar/styles.js index 7e7fe3b2..763a4178 100644 --- a/src/v2/components/NavBar/styles.js +++ b/src/v2/components/NavBar/styles.js @@ -1,4 +1,5 @@ import {makeStyles} from '@material-ui/core'; +import getColor from 'v2/utils/getColor'; export default makeStyles(theme => ({ root: { @@ -15,6 +16,9 @@ export default makeStyles(theme => ({ padding: '0 8px', marginBottom: 29, ...theme.mixins.toolbar, + [theme.breakpoints.down('sm')]: { + display: 'none', + }, }, item: { height: 102, @@ -32,4 +36,28 @@ export default makeStyles(theme => ({ width: 0, }, }, + drawerPaper: { + [theme.breakpoints.down('sm')]: { + width: '100%', + background: getColor('dark')(theme), + }, + }, + menuButton: { + marginLeft: 'auto', + display: 'none', + [theme.breakpoints.down('sm')]: { + display: 'block', + }, + }, + itemText: { + textTransform: 'uppercase', + marginLeft: 38, + fontSize: 15, + fontWeight: 'bold', + letterSpacing: 2.5, + display: 'none', + [theme.breakpoints.down('sm')]: { + display: 'block', + }, + }, })); diff --git a/src/v2/components/Social/styles.js b/src/v2/components/Social/styles.js index 2dc3e769..f54b3b70 100644 --- a/src/v2/components/Social/styles.js +++ b/src/v2/components/Social/styles.js @@ -7,7 +7,7 @@ export default makeStyles(theme => ({ flexDirection: 'column', alignItems: 'center', marginTop: 80, - [theme.breakpoints.down('md')]: { + [theme.breakpoints.down('sm')]: { flexDirection: 'row', justifyContent: 'center', }, @@ -24,7 +24,7 @@ export default makeStyles(theme => ({ width: 17, height: 'auto', }, - [theme.breakpoints.down('md')]: { + [theme.breakpoints.down('sm')]: { marginBottom: 0, marginRight: 22, }, diff --git a/src/v2/components/UI/SectionHeader/styles.js b/src/v2/components/UI/SectionHeader/styles.js index 72a50802..a3ca6c3a 100644 --- a/src/v2/components/UI/SectionHeader/styles.js +++ b/src/v2/components/UI/SectionHeader/styles.js @@ -1,6 +1,6 @@ import {makeStyles} from '@material-ui/core'; -export default makeStyles({ +export default makeStyles(theme => ({ root: { position: 'relative', marginBottom: 47, @@ -13,5 +13,8 @@ export default makeStyles({ position: 'absolute', left: -120, top: -7, + [theme.breakpoints.down('sm')]: { + display: 'none', + }, }, -}); +})); diff --git a/src/v2/components/Validators/All/index.jsx b/src/v2/components/Validators/All/index.jsx index e6572a50..d3539d99 100644 --- a/src/v2/components/Validators/All/index.jsx +++ b/src/v2/components/Validators/All/index.jsx @@ -6,7 +6,7 @@ import ValidatorsTable from '../Table'; const ValidatorsAll = () => { return ( - + ); }; diff --git a/src/v2/components/Validators/Table/index.jsx b/src/v2/components/Validators/Table/index.jsx index 9808a871..b4d97cc1 100644 --- a/src/v2/components/Validators/Table/index.jsx +++ b/src/v2/components/Validators/Table/index.jsx @@ -1,3 +1,5 @@ +// @flow + import React from 'react'; import { Typography, @@ -8,6 +10,7 @@ import { TableRow, Grid, } from '@material-ui/core'; +import cn from 'classnames'; import useMediaQuery from '@material-ui/core/useMediaQuery'; import {useTheme} from '@material-ui/core/styles'; import {observer} from 'mobx-react-lite'; @@ -17,7 +20,7 @@ import NodesStore from 'v2/stores/nodes'; import useStyles from './styles'; -const ValidatorsTable = () => { +const ValidatorsTable = ({vertical}: {vertical: boolean}) => { const classes = useStyles(); const theme = useTheme(); const showTable = useMediaQuery(theme.breakpoints.up('md')); @@ -41,10 +44,10 @@ const ValidatorsTable = () => { }; const renderCard = card => { return ( -
+
- {card.pubkey} +
{card.pubkey}
@@ -67,7 +70,7 @@ const ValidatorsTable = () => {
Validators - 129,948 + {nodes.length} See all > @@ -91,7 +94,9 @@ const ValidatorsTable = () => { ) : ( -
{map(renderCard)(nodes)}
+
+ {map(renderCard)(nodes)} +
)}
); diff --git a/src/v2/components/Validators/Table/styles.js b/src/v2/components/Validators/Table/styles.js index 62d8afb0..ef16e868 100644 --- a/src/v2/components/Validators/Table/styles.js +++ b/src/v2/components/Validators/Table/styles.js @@ -6,9 +6,10 @@ export default makeStyles(theme => ({ marginTop: 19, background: getColor('grey2')(theme), padding: '25px 44px', - [theme.breakpoints.down('md')]: { + [theme.breakpoints.down('sm')]: { padding: 0, paddingBottom: 27, + marginBottom: 50, }, }, head: { @@ -26,6 +27,10 @@ export default makeStyles(theme => ({ fontSize: 15, paddingTop: 18, paddingBottom: 18, + maxWidth: 0, + whiteSpace: 'nowrap', + textOverflow: 'ellipsis', + overflow: 'hidden', }, }, header: { @@ -36,7 +41,7 @@ export default makeStyles(theme => ({ marginRight: 35, }, marginBottom: 23, - [theme.breakpoints.down('md')]: { + [theme.breakpoints.down('sm')]: { padding: '10px 27px 0', marginBottom: 10, }, @@ -52,9 +57,6 @@ export default makeStyles(theme => ({ display: 'flex', alignItems: 'center', color: getColor('main')(theme), - whiteSpace: 'nowrap', - textOverflow: 'ellipsis', - overflow: 'hidden', '& span': { width: 33, height: 33, @@ -63,7 +65,12 @@ export default makeStyles(theme => ({ borderRadius: '50%', marginRight: 22, }, - [theme.breakpoints.down('md')]: { + '& div': { + whiteSpace: 'nowrap', + textOverflow: 'ellipsis', + overflow: 'hidden', + }, + [theme.breakpoints.down('sm')]: { marginBottom: 22, }, }, @@ -72,12 +79,24 @@ export default makeStyles(theme => ({ width: '100%', overflowX: 'auto', }, + vertical: { + [theme.breakpoints.down('sm')]: { + flexDirection: 'column', + }, + }, card: { padding: 17, background: '#505050', marginRight: 12, maxWidth: 326, }, + cardVertical: { + [theme.breakpoints.down('sm')]: { + marginBottom: 2, + marginRight: 0, + maxWidth: '100%', + }, + }, cardTitle: { fontSize: 12, textTransform: 'uppercase', diff --git a/src/v2/components/Validators/ValidatorsMap/index.jsx b/src/v2/components/Validators/ValidatorsMap/index.jsx index 5882054e..084dac03 100644 --- a/src/v2/components/Validators/ValidatorsMap/index.jsx +++ b/src/v2/components/Validators/ValidatorsMap/index.jsx @@ -12,7 +12,8 @@ import { import {map} from 'lodash/fp'; import nodesStore from 'v2/stores/nodes'; import MapTooltip from 'v2/components/UI/MapTooltip'; -import {mapStyle, markerStyle} from 'v2/theme'; +import theme, {mapStyle, markerStyle} from 'v2/theme'; +import getColor from 'v2/utils/getColor'; import useStyles from './styles'; @@ -22,6 +23,10 @@ const mapStyles = { pressed: mapStyle, }; +const markerCircleStyle = { + stroke: getColor('main')(theme), +}; + const ValidatorsMap = () => { const classes = useStyles(); const {mapMarkers} = nodesStore; @@ -49,14 +54,7 @@ const ValidatorsMap = () => { )} > - + ); @@ -74,18 +72,15 @@ const ValidatorsMap = () => { geography={`${process.env.PUBLIC_URL}/resources/world-50m-simplified.json`} > {(geographies, projection) => - geographies.map( - (geography, i) => - geography.id !== 'ATA' && ( - - ), - ) + geographies.map((geography, i) => ( + + )) } {map(renderMarker)(mapMarkers)} diff --git a/src/v2/components/Validators/index.jsx b/src/v2/components/Validators/index.jsx index 70abd832..fa3fbaa6 100644 --- a/src/v2/components/Validators/index.jsx +++ b/src/v2/components/Validators/index.jsx @@ -1,6 +1,6 @@ // @flow import {Container, Grid} from '@material-ui/core'; -import React from 'react'; +import React, {useEffect} from 'react'; import {observer} from 'mobx-react-lite'; import {map} from 'lodash/fp'; import Card from 'v2/components/UI/StatCard'; @@ -14,24 +14,28 @@ import useStyles from './styles'; const Validators = () => { const classes = useStyles(); - const {cluster, clusterChanges} = NodesStore; + const {cluster, fetchClusterInfo, totalBondedTokens} = NodesStore; + useEffect(() => { + fetchClusterInfo(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); const cards = [ { title: 'Total Circulating SOL', value: cluster.supply / Math.pow(2, 34).toFixed(2), - changes: clusterChanges.supply, + changes: '', period: 'since yesterday', }, { title: 'Total Bonded Tokens', - value: '100,000', - changes: '-2.45', + value: totalBondedTokens, + changes: '', period: 'since yesterday', }, { title: '# Active Validators', value: cluster.nodes.length, - changes: clusterChanges.nodes, + changes: '', period: 'since yesterday', }, ]; @@ -55,7 +59,7 @@ const Validators = () => { value={value} changes={() => (
-
{changes}%
+ {changes &&
{changes}%
}
{period}
)} @@ -75,11 +79,11 @@ const Validators = () => {
- + - - {map(renderStats)(cards)} + +
{map(renderStats)(cards)}
diff --git a/src/v2/components/Validators/styles.js b/src/v2/components/Validators/styles.js index 85de48d9..46ed864b 100644 --- a/src/v2/components/Validators/styles.js +++ b/src/v2/components/Validators/styles.js @@ -19,9 +19,24 @@ export default makeStyles(theme => ({ fontWeight: 'normal', }, card: { + minWidth: '1px', '&:not(:last-child)': { marginBottom: 15, }, + [theme.breakpoints.down('md')]: { + flex: 1, + '&:not(:last-child)': { + marginBottom: 0, + marginRight: 10, + }, + }, + [theme.breakpoints.down('sm')]: { + flex: 1, + '&:not(:last-child)': { + marginRight: 0, + marginBottom: 15, + }, + }, }, becomeBtn: { marginLeft: 'auto', @@ -29,4 +44,14 @@ export default makeStyles(theme => ({ display: 'none', }, }, + stats: { + display: 'flex', + flexDirection: 'column', + [theme.breakpoints.down('md')]: { + flexDirection: 'row', + }, + [theme.breakpoints.down('sm')]: { + flexDirection: 'column', + }, + }, })); diff --git a/src/v2/const.js b/src/v2/const.js deleted file mode 100644 index 25b390d8..00000000 --- a/src/v2/const.js +++ /dev/null @@ -1,4 +0,0 @@ -// TODO: Delete this file and use ../EndpointConfig.js instead -export const BLOCK_EXPLORER_API_BASE = `http://${process.env.REACT_APP_BASE_URL}:3001`; -export const BLOCK_EXPLORER_WS_API_BASE = `ws://${process.env.REACT_APP_BASE_URL}:3001`; -export const BLOCK_EXPLORER_RPC_URL = `http://${process.env.REACT_APP_BASE_URL}:8899`; diff --git a/src/v2/stores/nodes.js b/src/v2/stores/nodes.js index b87ab090..f05f1556 100644 --- a/src/v2/stores/nodes.js +++ b/src/v2/stores/nodes.js @@ -1,8 +1,18 @@ -import {eq, compose, keys, map, mapValues, pick} from 'lodash/fp'; -import {action, computed, decorate, observable, observe} from 'mobx'; +import { + sumBy, + eq, + get, + compose, + keys, + map, + mapValues, + pick, + merge, +} from 'lodash/fp'; +import {action, computed, decorate, observable, observe, flow} from 'mobx'; import {parseClusterInfo} from 'v2/utils/parseMessage'; - -import calcChanges from '../utils/calcChanges'; +import * as API from 'v2/api/stats'; +import calcChanges from 'v2/utils/calcChanges'; class NodesStore { cluster = { @@ -28,9 +38,14 @@ class NodesStore { } updateClusterInfo = data => { - this.cluster = parseClusterInfo(data); + this.cluster = merge(this.cluster, parseClusterInfo(data)); }; + fetchClusterInfo = flow(function*() { + const res = yield API.getClusterInfo(); + this.cluster = merge(this.cluster, res.data); + }); + get mapMarkers() { return map(({pubkey: name, gossip, lat, lng}) => ({ name, @@ -38,12 +53,20 @@ class NodesStore { coordinates: [lng, lat], }))(this.cluster.nodes); } + + get totalBondedTokens() { + return compose( + sumBy('stake'), + get('voting'), + )(this.cluster); + } } decorate(NodesStore, { cluster: observable, updateClusterInfo: action.bound, mapMarkers: computed, + fetchClusterInfo: action.bound, }); export default new NodesStore();