Browse Source

Validators style + Nominators handles

Joystream Stats 2 years ago
parent
commit
04dbcc5ece

+ 5 - 2
src/App.tsx

@@ -350,8 +350,7 @@ class App extends React.Component<IProps, IState> {
   }
 
   async fetchStakes(api: Api, era: number, validators: string[]) {
-    // TODO staking.bondedEras: Vec<(EraIndex,SessionIndex)>
-    const { stashes } = this.state;
+    const { members, stashes } = this.state;
     if (!stashes) return;
     stashes.forEach(async (validator: string) => {
       try {
@@ -364,6 +363,10 @@ class App extends React.Component<IProps, IState> {
         const data = await api.query.staking.erasStakers(era, validator);
         let { total, own, others } = data.toJSON();
         let { stakes = {} } = this.state;
+        others = others.map(({ who, value }) => {
+          const member = members.find((m) => m.rootKey === who);
+          return { who, value, member };
+        });
 
         stakes[validator] = { total, own, others, commission };
         this.save("stakes", stakes);

+ 16 - 38
src/components/Dashboard/Proposals.tsx

@@ -1,28 +1,30 @@
-import React from "react";
 import { Link } from "react-router-dom";
 import { RefreshCw } from "react-feather";
 import { ProposalTable } from "..";
 import {
-  AppBar,
   createStyles,
-  Grid,
   makeStyles,
+  Grid,
   Paper,
-  Theme,
+  AppBar,
   Toolbar,
   Typography,
+  Theme,
 } from "@material-ui/core";
 import { Council, Member, ProposalDetail, Post } from "../../types";
 
 const useStyles = makeStyles((theme: Theme) =>
   createStyles({
-    root: {
-      flexGrow: 1,
+    grid: { textAlign: "center", backgroundColor: "#000", color: "#fff" },
+    root: { flexGrow: 1, backgroundColor: "#4038FF" },
+    title: { textAlign: "left", flexGrow: 1 },
+    paper: {
+      textAlign: "center",
       backgroundColor: "#4038FF",
-    },
-    title: {
-      textAlign: "left",
-      flexGrow: 1,
+      color: "#fff",
+      minHeight: 500,
+      maxHeight: 500,
+      overflow: "auto",
     },
   })
 );
@@ -37,38 +39,14 @@ const Proposals = (props: {
   block: number;
   status: { council: Council };
 }) => {
-  const {
-    proposals,
-    validators,
-    councils,
-    members,
-    posts,
-    block,
-    status,
-  } = props;
   const classes = useStyles();
+  const { proposals, validators, councils, members, posts, block, status } =
+    props;
   const pending = proposals.filter((p) => p && p.result === "Pending");
 
   return (
-    <Grid
-      style={{
-        textAlign: "center",
-        backgroundColor: "#000",
-        color: "#fff",
-      }}
-      item
-      lg={6}
-    >
-      <Paper
-        style={{
-          textAlign: "center",
-          backgroundColor: "#4038FF",
-          color: "#fff",
-          minHeight: 500,
-          maxHeight: 500,
-          overflow: "auto",
-        }}
-      >
+    <Grid className={classes.grid} item lg={6}>
+      <Paper className={classes.paper}>
         <AppBar className={classes.root} position="static">
           <Toolbar>
             <Typography variant="h6" className={classes.title}>

+ 15 - 26
src/components/Dashboard/index.tsx

@@ -58,38 +58,27 @@ const Dashboard = (props: IProps) => {
             domain={domain}
             gridSize={6}
           />
+          <Validators
+            toggleStar={toggleStar}
+            councils={councils}
+            members={members}
+            posts={posts}
+            proposals={proposals}
+            nominators={nominators}
+            validators={validators}
+            stashes={stashes}
+            stars={stars}
+            stakes={stakes}
+            rewardPoints={rewardPoints}
+            tokenomics={tokenomics}
+            status={status}
+          />
           <Forum
             updateForum={props.updateForum}
             posts={posts}
             threads={threads}
             startTime={status.startTime}
           />
-          <Grid
-            style={{
-              textAlign: "center",
-              backgroundColor: "#000",
-              color: "#fff",
-            }}
-            item
-            lg={6}
-            sm={12}
-          >
-            <Validators
-              toggleStar={toggleStar}
-              councils={councils}
-              members={members}
-              posts={posts}
-              proposals={proposals}
-              nominators={nominators}
-              validators={validators}
-              stashes={stashes}
-              stars={stars}
-              stakes={stakes}
-              rewardPoints={rewardPoints}
-              tokenomics={tokenomics}
-              status={status}
-            />
-          </Grid>
         </Grid>
       </Container>
     </div>

+ 12 - 21
src/components/Members/MemberBox.tsx

@@ -5,8 +5,14 @@ import { Council, Post, ProposalDetail } from "../../types";
 import { Link } from "react-router-dom";
 import InfoTooltip from "../Tooltip";
 
-const shortName = (key = "") =>
-  `${key.slice(0, 5)}..${key.slice(key.length - 5)}`;
+const linkStyle = {
+  color: "#fff",
+  display: "block",
+  margin: 4,
+  border: "1px solid #fff",
+  borderRadius: "4px",
+  fontSize: 13,
+};
 
 const MemberBox = (props: {
   council: Council;
@@ -19,17 +25,8 @@ const MemberBox = (props: {
   placement: "left" | "bottom" | "right" | "top";
   validators: string[];
 }) => {
-  const {
-    account,
-    councils,
-    council,
-    member,
-    posts,
-    placement,
-    proposals,
-  } = props;
-
-  const handle = member ? member.handle : shortName(account);
+  const { councils, council, member, posts, placement, proposals } = props;
+  const handle = member?.handle || props.account;
   return (
     <InfoTooltip
       placement={placement}
@@ -48,14 +45,8 @@ const MemberBox = (props: {
     >
       <Link
         variant={"button"}
-        className="text-center"
-        style={{
-          color: "#fff",
-          display: "block",
-          margin: 4,
-          border: "1px solid #fff",
-          borderRadius: "4px",
-        }}
+        className="text-center p-2"
+        style={linkStyle}
         to={`/members/${handle}`}
       >
         {handle}

+ 1 - 2
src/components/Modals/EditKpi.tsx

@@ -1,7 +1,6 @@
 import { useState } from "react";
-import { textarea, Form, FormGroup } from "react-bootstrap";
+import {  Form, FormGroup } from "react-bootstrap";
 import { Button, Modal } from "react-bootstrap";
-//import moment from "moment";
 
 const EditKpi = (props) => {
   const [kpi, handleChange] = useState(props.kpi);

+ 1 - 1
src/components/Routes/index.tsx

@@ -107,7 +107,7 @@ const Routes = (props: IProps) => {
               <Route
                 path="/validators"
                 render={(routeprops) => (
-                  <Validators {...routeprops} {...props} />
+                  <Validators showList={true} {...routeprops} {...props} />
                 )}
               />
               <Route

+ 0 - 1
src/components/Timeline/Item.tsx

@@ -6,7 +6,6 @@ import gfm from "remark-gfm";
 
 const TimelineItem = (props: { event: Event; startTime: number }) => {
   const { category, date, text, link } = props.event;
-  const time = moment().format;
 
   const formatTime = (time: number) => {
     return moment(time).format("DD/MM/YYYY HH:mm");

+ 12 - 94
src/components/Validators/Content.tsx

@@ -1,50 +1,18 @@
-import React, { useState } from "react";
-import { Button } from "react-bootstrap";
 import Stats from "./MinMax";
-import Validator from "./Validator";
-import Waiting from "./Waiting";
+import ValidatorList from "./ValidatorList";
 import { Spinner } from "..";
-import { sortValidators } from "./util";
 
 const Content = (props: IState) => {
-  const [sortBy, setSortBy] = useState("totalStake");
-
-  const [showWaiting, setShowWaiting] = useState(false);
-  const [showValidators, setShowValidators] = useState(false);
-
-  const toggleValidators = () => setShowValidators((prev) => !prev);
-
-  const toggleWaiting = () => setShowWaiting((prev) => !prev);
-
-  const {
-    getMember = () => {},
-    councils,
-    members,
-    posts,
-    proposals,
-    validators,
-    nominators,
-    stashes,
-    stars,
-    rewardPoints,
-    status,
-    stakes,
-    tokenomics,
-  } = props;
-
+  const { validators, stashes, stars, status, stakes, tokenomics } = props;
   if (!status?.block || !validators.length) return <Spinner />;
 
-  const { lastReward, block, era, startTime } = status;
-
+  const { block, era } = status;
+  const waiting = stashes.filter((s) => !stars[s] && !validators.includes(s));
   const issued = tokenomics ? Number(tokenomics.totalIssuance) : 0;
   const price = tokenomics ? Number(tokenomics.price) : 0;
 
-  const starred = stashes.filter((v) => stars[v]);
-  const unstarred = validators.filter((v) => !stars[v]);
-  const waiting = stashes.filter((s) => !stars[s] && !validators.includes(s));
-
   return (
-    <>
+    <div className="p-3">
       <Stats
         block={block}
         era={era}
@@ -52,66 +20,16 @@ const Content = (props: IState) => {
         issued={issued}
         price={price}
         validators={validators}
-        nominators={nominators.length}
+        nominators={props.nominators.length}
         waiting={waiting.length}
         reward={status.lastReward}
       />
-      <div className="d-flex flex-column mt-3">
-        {sortValidators(sortBy, starred, stakes, rewardPoints).map((v) => (
-          <Validator
-            key={v}
-            sortBy={setSortBy}
-            starred={stars[v] ? `teal` : undefined}
-            toggleStar={props.toggleStar}
-            startTime={startTime}
-            validator={v}
-            reward={lastReward / validators.length}
-            councils={councils}
-            council={status.council}
-            member={getMember(v)}
-            posts={posts}
-            proposals={proposals}
-            validators={validators}
-            stakes={stakes}
-            rewardPoints={rewardPoints}
-          />
-        ))}
-
-        <Button onClick={() => toggleValidators()}>
-          Toggle {unstarred.length} validators
-        </Button>
-
-        {showValidators &&
-          sortValidators(sortBy, unstarred, stakes, rewardPoints).map((v) => (
-            <Validator
-              key={v}
-              sortBy={setSortBy}
-              starred={stars[v] ? `teal` : undefined}
-              toggleStar={props.toggleStar}
-              startTime={startTime}
-              validator={v}
-              reward={lastReward / validators.length}
-              councils={councils}
-              council={status.council}
-              member={getMember(v)}
-              posts={posts}
-              proposals={proposals}
-              validators={validators}
-              stakes={stakes}
-              rewardPoints={rewardPoints}
-            />
-          ))}
-
-        <Waiting
-          toggleWaiting={toggleWaiting}
-          show={showWaiting}
-          waiting={waiting}
-          posts={posts}
-          proposals={proposals}
-          members={members}
-        />
-      </div>
-    </>
+      <ValidatorList
+        toggleStar={props.toggleStar}
+        waiting={waiting}
+        {...props}
+      />
+    </div>
   );
 };
 

+ 11 - 5
src/components/Validators/MinMax.tsx

@@ -1,8 +1,15 @@
-import { Table } from "@material-ui/core";
-import React from "react";
+import { createStyles, makeStyles } from "@material-ui/core";
+import { Table } from "react-bootstrap";
 import { Link } from "react-router-dom";
 import { Stakes } from "../../types";
 
+const useStyles = makeStyles((theme: Theme) =>
+  createStyles({
+    root: { flexGrow: 1, backgroundColor: "#4038FF" },
+    title: { textAlign: "left", flexGrow: 1, padding: "3px" },
+  })
+);
+
 const dollar = (d: number) => (d > 0 ? `$ ${d.toFixed(2)}` : "");
 
 const MinMax = (props: {
@@ -16,6 +23,7 @@ const MinMax = (props: {
   reward: number;
   price: number;
 }) => {
+  const classes = useStyles();
   const { issued, stakes, validators, waiting, reward, price } = props;
   if (!stakes || !Object.values(stakes).length) return <span />;
 
@@ -37,9 +45,7 @@ const MinMax = (props: {
   const validatorReward = reward ? reward / validators.length : 0;
 
   return (
-    <Table
-      style={{ textAlign: "center", backgroundColor: "#4038FF", color: "#fff" }}
-    >
+    <Table className="text-center" style={{ color: "#fff" }}>
       <tbody>
         <tr>
           <td {...name}>Validators</td>

+ 2 - 4
src/components/Validators/Nominators.tsx

@@ -2,8 +2,6 @@ import React from "react";
 import User from "../User";
 import { Stake } from "../../types";
 
-// TODO use MemberBox after refactor
-
 const Reward = (reward: number) =>
   reward > 0 ? (
     <span className="text-warning mx-1">+{reward.toFixed(0)}</span>
@@ -27,7 +25,7 @@ const Nominators = (props: {
   nominators.forEach((n) => (sum += n.value));
 
   return (
-    <table onClick={() => sortBy("othersStake")}>
+    <table className="text-center" onClick={() => sortBy("othersStake")}>
       <tbody>
         {nominators.length > 1 && (
           <tr>
@@ -50,7 +48,7 @@ const Nominators = (props: {
                 {Reward(reward * (n.value / sum))}
               </td>
               <td>
-                <User id={n.who} handle={n.who} />
+                <User id={n.who} handle={n.member?.handle || n.who} />
               </td>
             </tr>
           ))}

+ 66 - 85
src/components/Validators/Validator.tsx

@@ -2,14 +2,7 @@ import React, { Component } from "react";
 import { Activity, Star } from "react-feather";
 import Nominators from "./Nominators";
 import MemberBox from "../Members/MemberBox";
-import {
-  Member,
-  Post,
-  ProposalDetail,
-  Seat,
-  Stakes,
-  RewardPoints,
-} from "../../types";
+import { ProposalDetail, Seat, Stakes, RewardPoints } from "../../types";
 import { domain } from "../../config";
 
 interface IProps {
@@ -17,8 +10,6 @@ interface IProps {
   sortBy: (sortBy: string) => void;
   validator: string;
   councils: Seat[][];
-  members: Member[];
-  posts: Post[];
   proposals: ProposalDetail[];
   validators: string[];
   startTime: number;
@@ -34,10 +25,7 @@ interface IState {
 }
 
 const fNum = (n: number) =>
-  n.toLocaleString(undefined, {
-    minimumFractionDigits: 2,
-    maximumFractionDigits: 2,
-  });
+  n.toLocaleString(undefined, { maximumFractionDigits: 0 });
 
 class Validator extends Component<IProps, IState> {
   constructor(props: IProps) {
@@ -94,80 +82,73 @@ class Validator extends Component<IProps, IState> {
     }
 
     return (
-      <div className="mt-2 d-flex flex-row justify-content-around">
-        <div
-          className="col-1 text-right"
-          onClick={() => sortBy("points")}
-          title="era points"
-        >
-          {points}
-          <a
-            href={`${domain}/#/staking/query/${validator}`}
-            title="Show Stats (External)"
-          >
-            <Activity width={15} />
-          </a>
-        </div>
-        <div className="col-2 text-right">
-          <MemberBox
-            id={0}
-            account={validator}
-            placement={"right"}
-            councils={councils}
-            council={council}
-            member={member}
-            posts={posts}
-            proposals={proposals}
-            startTime={startTime}
-            validators={this.props.validators}
-          />
-          <Star
-            width={15}
-            color={"black"}
-            fill={starred ? "black" : "teal"}
-            className="ml-2 mb-2"
-            onClick={() => toggleStar(validator)}
-          />
-        </div>
+      <div className="p-2 col-12 col-md-6" style={{ fontSize: 11 }}>
+        <MemberBox
+          account={validator}
+          placement={"right"}
+          councils={councils}
+          council={council}
+          member={member}
+          posts={posts}
+          proposals={proposals}
+          startTime={startTime}
+          validators={this.props.validators}
+        />
 
-        <div
-          className="col-1 text-right"
-          onClick={() => sortBy("commission")}
-          title="commission"
-        >
-          {commission}
-        </div>
-        <div
-          className="col-2 text-black text-right"
-          onClick={() => sortBy("totalStake")}
-          title="total stake"
-        >
-          {totalStake > 0 && fNum(totalStake)}
-        </div>
-        <div
-          className="col-2 text-right"
-          onClick={() => sortBy("ownStake")}
-          title="own stake"
-        >
-          {ownStake > 0 && fNum(ownStake)}
-        </div>
-        {ownReward ? (
-          <div className="col-1 text-warning" title="reward">
-            +{Math.round(ownReward)}
+        <div className="mt-2 d-flex flex-row justify-content-around">
+          <div onClick={() => sortBy("points")} title="era points">
+            {points}
+          </div>
+          <div onClick={() => sortBy("commission")} title="commission">
+            {commission}
+          </div>
+          <div
+            className="col-2 text-black text-right"
+            onClick={() => sortBy("totalStake")}
+            title="total stake"
+          >
+            {totalStake > 0 && fNum(totalStake)}
+          </div>
+          <div
+            className="col-2 text-right"
+            onClick={() => sortBy("ownStake")}
+            title="own stake"
+          >
+            {ownStake > 0 && fNum(ownStake)}
+          </div>
+          {ownReward ? (
+            <div className="col-1 text-warning" title="reward">
+              +{Math.round(ownReward)}
+            </div>
+          ) : (
+            <div />
+          )}
+          <div>
+            <Star
+              width={15}
+              color={"white"}
+              fill={starred ? "white" : "#4038FF"}
+              onClick={() => toggleStar(validator)}
+            />
+          </div>
+          <div>
+            <a
+              href={`${domain}/#/staking/query/${validator}`}
+              title="Show Stats (External)"
+            >
+              <Activity width={15} />
+            </a>
           </div>
-        ) : (
-          <div />
-        )}
-        <div className="d-none d-md-block col-3 mb-1 text-left">
-          <Nominators
-            fNum={fNum}
-            reward={nomReward}
-            toggleExpand={this.toggleExpandNominators}
-            sortBy={sortBy}
-            expand={expandNominators}
-            nominators={stake ? stake.others : undefined}
-          />
         </div>
+
+        <Nominators
+          fNum={fNum}
+          reward={nomReward}
+          toggleExpand={this.toggleExpandNominators}
+          sortBy={sortBy}
+          expand={expandNominators}
+          nominators={stake ? stake.others : undefined}
+        />
       </div>
     );
   }

+ 31 - 38
src/components/Validators/index.tsx

@@ -1,66 +1,59 @@
-import React from "react";
 import Content from "./Content";
 import { IState } from "../../types";
 import {
-  AppBar,
   createStyles,
-  Grid,
   makeStyles,
+  Grid,
   Paper,
-  Theme,
+  AppBar,
   Toolbar,
   Typography,
+  Theme,
 } from "@material-ui/core";
 
 const useStyles = makeStyles((theme: Theme) =>
   createStyles({
-    root: {
-      flexGrow: 1,
-      backgroundColor: "#4038FF",
+    grid: {
+      width: "100%",
+      textAlign: "center",
+      backgroundColor: "#000",
+      color: "#fff",
     },
-    title: {
-      textAlign: "left",
-      flexGrow: 1,
+    root: { flexGrow: 1, backgroundColor: "#4038FF" },
+    title: { textAlign: "left", flexGrow: 1 },
+    paper: {
+      backgroundColor: "#4038FF",
+      color: "#fff",
     },
   })
 );
 
 const Validators = (props: IState) => {
   const classes = useStyles();
-
-  return (
-    <Grid
-      style={{
-        textAlign: "center",
-        backgroundColor: "#000",
-        color: "#fff",
-      }}
-      item
-      lg={12}
-    >
-      <Paper
-        style={{
-          textAlign: "center",
-          backgroundColor: "#4038FF",
-          color: "#fff",
-          minHeight: 500,
-          maxHeight: 500,
-          overflow: "auto",
-        }}
-      >
+  return props.showList ? (
+    <div className="m-3">
+      <Paper className={classes.paper}>
         <AppBar className={classes.root} position="static">
           <Toolbar>
-            <Typography
-              className={classes.title}
-              variant="h6"
-              onClick={() => toggleValidators()}
-            >
+            <Typography variant="h6" className={classes.title}>
               Validator Stats
             </Typography>
           </Toolbar>
         </AppBar>
-
-        <Content getMember={props.getMember} {...props} />
+        <Content toggleStar={props.toggleStar} {...props} />
+      </Paper>
+    </div>
+  ) : (
+    <Grid className={classes.grid} item lg={6}>
+      <Paper className={classes.paper}>
+        <AppBar className={classes.root} position="static">
+          <Toolbar>
+            <Typography variant="h6" className={classes.title}>
+              Validator Stats
+            </Typography>
+          </Toolbar>
+        </AppBar>
+        <Content toggleStar={props.toggleStar} {...props} />
       </Paper>
     </Grid>
   );