diff --git a/src/v2/components/TourDeSol/Ranking/index.jsx b/src/v2/components/TourDeSol/Ranking/index.jsx index 04046bac..50788bf8 100644 --- a/src/v2/components/TourDeSol/Ranking/index.jsx +++ b/src/v2/components/TourDeSol/Ranking/index.jsx @@ -4,6 +4,7 @@ import {get, map, maxBy, compose} from 'lodash/fp'; import HelpLink from 'v2/components/HelpLink'; import NodesStore from 'v2/stores/nodes'; import {ReactComponent as BicycleIcon} from 'v2/assets/icons/bicycle.svg'; +import Avatar from 'v2/components/UI/Avatar'; import useStyles from './styles'; @@ -18,9 +19,13 @@ const Ranking = () => { const renderNode = node => { const position = (node.stake * 100) / maxVal; + const {identity = {}, nodePubkey} = node; return ( -
  • -
    {node.nodePubkey}
    +
  • +
    + + {identity.name || nodePubkey} +
    ({ color: getColor('main')(theme), marginRight: 20, flexShrink: 0, - '&::before': { - content: '""', - display: 'inline-block', - verticalAlign: 'middle', - width: 33, - height: 33, - background: getColor('main')(theme), - borderRadius: '50%', - marginRight: 22, + display: 'flex', + alignItems: 'center', + '& div:first-child': { + marginRight: 15, }, }, bar: { diff --git a/src/v2/components/TourDeSol/Table/index.jsx b/src/v2/components/TourDeSol/Table/index.jsx index 5fc0ba10..cfcd6374 100644 --- a/src/v2/components/TourDeSol/Table/index.jsx +++ b/src/v2/components/TourDeSol/Table/index.jsx @@ -18,6 +18,7 @@ import {Link} from 'react-router-dom'; import {map} from 'lodash/fp'; import NodesStore from 'v2/stores/nodes'; import getUptime from 'v2/utils/getUptime'; +import Avatar from 'v2/components/UI/Avatar'; import HelpLink from '../../HelpLink'; import useStyles from './styles'; @@ -30,38 +31,37 @@ const ValidatorsTable = ({separate}: {separate: boolean}) => { const renderRow = row => { const uptime = getUptime(row); + const {identity = {}, nodePubkey, stake} = row; return ( - + 1 - - -
    {row.nodePubkey}
    + + +
    {identity.name || nodePubkey}
    - {row.stake} + {stake} {uptime}%
    ); }; const renderCard = card => { const uptime = getUptime(card); + const {identity = {}, nodePubkey, stake} = card; return (
    - -
    {card.nodePubkey}
    + +
    {identity.name || nodePubkey}
    Stake
    -
    {card.stake}
    +
    {stake}
    Uptime
    diff --git a/src/v2/components/TourDeSol/Table/styles.js b/src/v2/components/TourDeSol/Table/styles.js index ea75a43e..ea0aae26 100644 --- a/src/v2/components/TourDeSol/Table/styles.js +++ b/src/v2/components/TourDeSol/Table/styles.js @@ -60,15 +60,10 @@ export default makeStyles(theme => ({ alignItems: 'center', color: getColor('main')(theme), textDecoration: 'none', - '& span': { - width: 33, - height: 33, - flexShrink: 0, - background: getColor('main')(theme), - borderRadius: '50%', - marginRight: 22, - }, '& div': { + '&:first-child': { + marginRight: 22, + }, whiteSpace: 'nowrap', textOverflow: 'ellipsis', overflow: 'hidden', diff --git a/src/v2/components/UI/Avatar/index.jsx b/src/v2/components/UI/Avatar/index.jsx new file mode 100644 index 00000000..6922e2ff --- /dev/null +++ b/src/v2/components/UI/Avatar/index.jsx @@ -0,0 +1,31 @@ +// @flow +import React from 'react'; +import BaseAvatar from '@material-ui/core/Avatar'; +import theme from 'v2/theme'; +import getColor from 'v2/utils/getColor'; + +const Avatar = ({ + avatarUrl = '', + name = '', + width = 33, + height = 33, +}: { + avatarUrl: string, + name: string, +}) => { + const initials = name.charAt(0); + const avatarStyle = { + backgroundColor: getColor('main')(theme), + color: getColor('white')(theme), + width, + height, + }; + + return ( + + {!avatarUrl && initials} + + ); +}; + +export default Avatar; diff --git a/src/v2/components/Validators/Detail/index.jsx b/src/v2/components/Validators/Detail/index.jsx index 30b850ec..5e2c248b 100644 --- a/src/v2/components/Validators/Detail/index.jsx +++ b/src/v2/components/Validators/Detail/index.jsx @@ -3,7 +3,7 @@ import {Container, useTheme} from '@material-ui/core'; import {observer} from 'mobx-react-lite'; import useMediaQuery from '@material-ui/core/useMediaQuery/useMediaQuery'; import React, {useEffect} from 'react'; -import {map, find, compose, mergeWith} from 'lodash/fp'; +import {map, find} from 'lodash/fp'; import {Match} from 'react-router-dom'; import {CopyToClipboard} from 'react-copy-to-clipboard'; import { @@ -20,6 +20,8 @@ import theme, {mapStyle, markerStyle} from 'v2/theme'; import MapTooltip from 'v2/components/UI/MapTooltip'; import HelpLink from 'v2/components/HelpLink'; import getColor from 'v2/utils/getColor'; +import Button from 'v2/components/UI/Button'; +import Avatar from 'v2/components/UI/Avatar'; import {ReactComponent as CopyIcon} from '../../../assets/icons/copy.svg'; import Mixpanel from '../../../mixpanel'; @@ -36,9 +38,7 @@ const markerCircleStyle = { }; const ValidatorsDetail = ({match}: {match: Match}) => { - const { - cluster: {voting, cluster}, - } = NodesStore; + const {validators} = NodesStore; const classes = useStyles(); const theme = useTheme(); @@ -49,16 +49,13 @@ const ValidatorsDetail = ({match}: {match: Match}) => { Mixpanel.track(`Clicked Validator ${params.id}`); }, [params.id]); - const currentNode = find({pubkey: params.id})(cluster); - if (!currentNode) { + const node = find({nodePubkey: params.id})(validators); + + if (!node) { return
    Loading...
    ; } - const node = compose( - mergeWith(currentNode, { - coordinates: [currentNode.lng, currentNode.lat], - }), - find({nodePubkey: params.id}), - )(voting); + + const {nodePubkey, gossip, stake, commission, identity = {}} = node; const renderMarker = () => ( @@ -66,8 +63,8 @@ const ValidatorsDetail = ({match}: {match: Match}) => { classes={{tooltip: classes.tooltip}} title={() => ( <> -
    NODE: {node.nodePubkey}
    -
    Gossip: {node.gossip}
    +
    NODE: {nodePubkey}
    +
    Gossip: {gossip}
    )} > @@ -80,17 +77,17 @@ const ValidatorsDetail = ({match}: {match: Match}) => { { label: 'Website', hint: '', - value: 'TODO', + value: identity.website || '', }, { label: 'Voting power', hint: '', - value: `${node.stake}`, + value: stake, }, { label: 'Address', hint: '', - value: `${node.pubkey}`, + value: nodePubkey, }, { label: 'Missed blocks', @@ -100,17 +97,17 @@ const ValidatorsDetail = ({match}: {match: Match}) => { { label: 'keybase', hint: '', - value: 'TODO', + value: identity.keybaseUsername || '', }, { label: 'commission', hint: '', - value: `${node.commission}%`, + value: `${commission}%`, }, { label: 'details', hint: '', - value: 'TODO', + value: identity.details || '', }, { label: 'Amount of delegated sol', @@ -147,15 +144,26 @@ const ValidatorsDetail = ({match}: {match: Match}) => { {!isMobile && (
    - - {node.nodePubkey} - + + {identity.name || nodePubkey} +
    )} +
    + +
      @@ -165,7 +173,7 @@ const ValidatorsDetail = ({match}: {match: Match}) => {
      - {node.nodePubkey} + {nodePubkey}
      diff --git a/src/v2/components/Validators/Detail/styles.js b/src/v2/components/Validators/Detail/styles.js index 99991f31..10e53ad2 100644 --- a/src/v2/components/Validators/Detail/styles.js +++ b/src/v2/components/Validators/Detail/styles.js @@ -15,23 +15,31 @@ export default makeStyles(theme => ({ alignItems: 'center', marginLeft: 40, marginRight: 'auto', + flexShrink: 1, + minWidth: 1, [theme.breakpoints.down('sm')]: { marginLeft: 0, marginTop: 5, }, - '& > span': { - width: 33, - height: 33, - flexShrink: 0, - background: getColor('main')(theme), - borderRadius: '50%', - marginRight: 22, + '& > span:nth-child(2)': { + overflow: 'hidden', + textOverflow: 'ellipsis', + whiteSpace: 'nowrap', + marginLeft: 22, }, - '& div': { + '& div:last-child': { cursor: 'pointer', marginLeft: 14, }, }, + headerBtn: { + marginLeft: 40, + whiteSpace: 'nowrap', + [theme.breakpoints.down('sm')]: { + marginLeft: 0, + marginTop: 5, + }, + }, spec: { display: 'flex', flexWrap: 'wrap', diff --git a/src/v2/components/Validators/Table/index.jsx b/src/v2/components/Validators/Table/index.jsx index 1cba06d0..b247f7fd 100644 --- a/src/v2/components/Validators/Table/index.jsx +++ b/src/v2/components/Validators/Table/index.jsx @@ -18,6 +18,7 @@ import {Link} from 'react-router-dom'; import {map} from 'lodash/fp'; import NodesStore from 'v2/stores/nodes'; import getUptime from 'v2/utils/getUptime'; +import Avatar from 'v2/components/UI/Avatar'; import useStyles from './styles'; @@ -28,42 +29,41 @@ const ValidatorsTable = ({separate}: {separate: boolean}) => { const {validators} = NodesStore; const renderRow = row => { const uptime = getUptime(row); + const {identity = {}, nodePubkey, stake, commission} = row; return ( - + - - -
      {row.nodePubkey}
      + + +
      {identity.name || nodePubkey}
      - {row.stake} Lamports - {row.commission} + {stake} Lamports + {commission} {uptime}%
      ); }; const renderCard = card => { const uptime = getUptime(card); + const {identity = {}, nodePubkey, stake, commission} = card; return (
      - - -
      {card.nodePubkey}
      + + +
      {identity.name || nodePubkey}
      Stake
      -
      {card.stake} Lamports
      +
      {stake} Lamports
      Commission
      -
      {card.commission}
      +
      {commission}
      Uptime
      diff --git a/src/v2/components/Validators/Table/styles.js b/src/v2/components/Validators/Table/styles.js index ff7dd4cc..85404efe 100644 --- a/src/v2/components/Validators/Table/styles.js +++ b/src/v2/components/Validators/Table/styles.js @@ -58,15 +58,10 @@ export default makeStyles(theme => ({ alignItems: 'center', color: getColor('main')(theme), textDecoration: 'none', - '& span': { - width: 33, - height: 33, - flexShrink: 0, - background: getColor('main')(theme), - borderRadius: '50%', - marginRight: 22, - }, '& div': { + '&:first-child': { + marginRight: 15, + }, whiteSpace: 'nowrap', textOverflow: 'ellipsis', overflow: 'hidden', diff --git a/src/v2/stores/nodes.js b/src/v2/stores/nodes.js index b188fa96..2b430f14 100644 --- a/src/v2/stores/nodes.js +++ b/src/v2/stores/nodes.js @@ -57,10 +57,18 @@ class Store { } get validators() { - return map(vote => ({ - ...vote, - uptime: find({votePubkey: vote.votePubkey})(this.cluster.uptime), - }))(this.cluster.voting); + return map(vote => { + const {lng, lat, gossip} = find({pubkey: vote.nodePubkey})( + this.cluster.cluster, + ); + return { + ...vote, + coordinates: [lng, lat], + gossip, + uptime: find({votePubkey: vote.votePubkey})(this.cluster.uptime), + identity: find({pubkey: vote.nodePubkey})(this.cluster.identities), + }; + })(this.cluster.voting); } get totalBondedTokens() { diff --git a/src/v2/utils/parseMessage.js b/src/v2/utils/parseMessage.js index fca067ea..34592896 100644 --- a/src/v2/utils/parseMessage.js +++ b/src/v2/utils/parseMessage.js @@ -42,15 +42,23 @@ export function parseBlock(message) { } export function parseClusterInfo(data) { - const {voting, cluster: gossip, supply, feeCalculator} = JSON.parse(data); + const { + voting, + cluster: gossip, + supply, + feeCalculator, + identities, + } = JSON.parse(data); + const nodes = map(g => ({ ...g, - voteAccount: find({nodePubkey: g.pubKey}, voting), + voteAccount: find({nodePubkey: g.pubKey})(voting), }))(gossip); return { nodes, supply, feeCalculator, + identities, }; }