Browse Source

election countdown + stage announcement

Joystream Stats 4 years ago
parent
commit
6975f70223

+ 84 - 7
src/App.tsx

@@ -13,7 +13,7 @@ import { types } from "@joystream/types";
 import { Seat } from "@joystream/types/augment/all/types";
 import { ApiPromise, WsProvider } from "@polkadot/api";
 import { AccountId, Header } from "@polkadot/types/interfaces";
-import { MemberId, Membership } from "@joystream/types/members";
+import { MemberId } from "@joystream/types/members";
 
 interface IProps {}
 
@@ -21,6 +21,7 @@ const initialState = {
   blocks: [],
   now: 0,
   block: 0,
+  termEndsAt: 0,
   loading: true,
   nominators: [],
   validators: [],
@@ -46,6 +47,16 @@ class App extends React.Component<IProps, IState> {
     let blocks: Block[] = [];
     let lastBlock: Block = { id: 0, timestamp: 0, duration: 6 };
 
+    let termEndsAt = Number((await api.query.council.termEndsAt()).toJSON());
+    let round: number = Number(
+      (await api.query.councilElection.round()).toJSON()
+    );
+    let stage: any = await api.query.councilElection.stage();
+    let councilElection = { termEndsAt, stage: stage.toJSON(), round };
+    let councils = this.calculatePreviousCouncils(councilElection);
+    this.setState({ councilElection, councils });
+    let stageEndsAt: number = termEndsAt;
+
     // let channels = [];
     // channels[0] = await get.currentChannelId(api);
     // let posts = [];
@@ -82,6 +93,21 @@ class App extends React.Component<IProps, IState> {
         this.setState({ proposalComments: Number(postCount) });
 
         lastBlock = block;
+
+        // check election stage
+        if (id < termEndsAt || id < stageEndsAt) return;
+        const json = stage.toJSON();
+        const key = Object.keys(json)[0];
+        stageEndsAt = json[key];
+        console.log(id, stageEndsAt, json, key);
+
+        // TODO duplicate code
+        termEndsAt = Number((await api.query.council.termEndsAt()).toJSON());
+        round = Number((await api.query.councilElection.round()).toJSON());
+        stage = await api.query.councilElection.stage();
+        councilElection = { termEndsAt, stage: stage.toJSON(), round };
+        councils = this.calculatePreviousCouncils(councilElection);
+        this.setState({ councilElection, councils });
       }
     );
 
@@ -106,6 +132,7 @@ class App extends React.Component<IProps, IState> {
 
   async fetchCouncil(api: Api) {
     const council: any = await api.query.council.activeCouncil();
+    this.setState({ council });
     this.save(`council`, council);
     council.map((seat: Seat) => this.fetchHandle(api, seat.member));
   }
@@ -130,10 +157,7 @@ class App extends React.Component<IProps, IState> {
     const { proposals } = this.state;
     const proposal = proposals.find((p) => p && p.id === proposalId);
     if (!proposal) return;
-    const { id, createdAt, votes } = proposal;
-
-    //let totalVotes = 0;
-    //Object.keys(votes).map((key) => (totalVotes += votes[key]));
+    const { id, createdAt } = proposal;
 
     const council = this.getCouncilAtBlock(createdAt);
 
@@ -162,8 +186,61 @@ class App extends React.Component<IProps, IState> {
     return vote.toHuman();
   }
 
-  getCouncilAtBlock(block: number) {
-    // TODO
+  calculatePreviousCouncils(council: {
+    stage: any;
+    round: number;
+    termEndsAt: number;
+  }) {
+    const rounds = [
+      [0, 0],
+      [57601, 259201, 201601], // r 144000
+      [259201, 460801], // r 201600
+      [460801, 662401], // r 201600
+      [662401, 864001], // r 201600
+      [864001, 1065601, 1008001], // 144000
+      [1065601, 1267201, 1209601], // 144000
+    ];
+
+    let councils = [];
+
+    const termDuration = 144000;
+    const announcingPeriod = 28800;
+    const votingPeriod = 14400;
+    const revealingPeriod = 14400;
+    const startToStart =
+      termDuration + announcingPeriod + votingPeriod + revealingPeriod; // 201600
+
+    const { stage, termEndsAt, round } = council;
+    let startsAt = termEndsAt - termDuration;
+    let endsAt = startsAt + startToStart;
+
+    for (let r = stage ? round - 1 : round; startsAt > 0; r--) {
+      if (rounds[r]) {
+        if (rounds[r][0] !== startsAt)
+          console.log(`wrong start`, round, rounds[r][0], startsAt);
+        if (rounds[r][1] !== endsAt)
+          console.log(`wrong end`, round, rounds[r][1], endsAt);
+      }
+      councils.push({ round: r, endsAt, startsAt, seats: [] }); // TODO
+      startsAt = startsAt - startToStart;
+      endsAt = startsAt + startToStart;
+    }
+    return councils;
+  }
+
+  getCouncilAtBlock(block: number): Seat[] {
+    const { councils } = this.state;
+    try {
+      for (let round = councils.length; round > 0; round--) {
+        if (!councils[round]) continue;
+        if (block > councils[round].start) {
+          console.log(`block in council`, block, round);
+          return councils[round].seats;
+        }
+      }
+    } catch (e) {
+      console.log(`failed to find council at block`, block, e);
+    }
     return this.state.council;
   }
 

+ 46 - 0
src/components/Council/ElectionStatus.tsx

@@ -0,0 +1,46 @@
+import React from "react";
+import { domain } from "../../config";
+
+const ElectionStage = (props: any) => {
+  const { block, stage, termEndsAt } = props;
+
+  if (!stage) {
+    const blocks = termEndsAt - block;
+    const seconds = blocks * 6;
+    const days = Math.floor(seconds / 86400);
+    const hours = Math.floor((seconds - days * 86400) / 3600);
+    const minutes = Math.floor(seconds / 60);
+    const counter = days ? `${days}d` : hours ? `${hours}h` : `${minutes}min`;
+    return <div>Next election in {counter}</div>;
+  }
+
+  let stageString = Object.keys(JSON.parse(JSON.stringify(stage)))[0];
+
+  if (stageString === "Announcing")
+    return <a href={`${domain}/#/council/applicants`}>Apply now!</a>;
+
+  if (stageString === "Voting")
+    return <a href={`${domain}/#/council/applicants`}>Vote now!</a>;
+
+  if (stageString === "Revealing")
+    return <a href={`${domain}/#/council/votes`}>Reveal your vote!</a>;
+
+  return <div>{JSON.stringify(stage)}</div>;
+};
+
+const ElectionStatus = (props: {
+  councilElection?: { termEndsAt: number; round: number; stage: any };
+  block: number;
+}) => {
+  const { councilElection, block } = props;
+
+  if (!councilElection) return <div></div>;
+
+  return (
+    <div className="position-absolute text-left text-light">
+      <ElectionStage block={block} {...councilElection} />
+    </div>
+  );
+};
+
+export default ElectionStatus;

+ 5 - 2
src/components/Council/index.tsx

@@ -1,5 +1,6 @@
 import React from "react";
 import { Link } from "react-router-dom";
+import ElectionStatus from "./ElectionStatus";
 import User from "../User";
 import { Seat } from "../../types";
 import Loading from "../Loading";
@@ -7,12 +8,15 @@ import Loading from "../Loading";
 const Council = (props: {
   council: Seat[];
   handles: { [key: string]: string };
+  councilElection?: any;
+  block: number;
 }) => {
-  const { council, handles } = props;
+  const { council, handles, block, councilElection } = props;
   const half = Math.floor(council.length / 2);
 
   return (
     <div className="box">
+      <ElectionStatus block={block} councilElection={councilElection} />
       <h3>Council</h3>
 
       {(council.length && (
@@ -34,7 +38,6 @@ const Council = (props: {
         </div>
       )) || <Loading />}
       <hr />
-
       <Link to={`/tokenomics`}>Reports</Link>
     </div>
   );

+ 13 - 6
src/components/Dashboard/index.tsx

@@ -7,27 +7,34 @@ import { IState } from "../../types";
 import Loading from "../Loading";
 
 const Dashboard = (props: IState) => {
+  const { block, council, domain, handles, proposals } = props;
+
   return (
     <div className="w-100 flex-grow-1 d-flex align-items-center justify-content-center d-flex flex-column">
       <div className="title">
         <h1>
-          <a href={props.domain}>Joystream</a>
+          <a href={domain}>Joystream</a>
         </h1>
       </div>
       <div className="box mt-3">
         <h3>latest block</h3>
-        {props.block ? props.block : <Loading />}
+        {block ? block : <Loading />}
       </div>
       <div className="box">
         <h3>Active Proposals</h3>
-        <ActiveProposals block={props.block} proposals={props.proposals} />
+        <ActiveProposals block={block} proposals={proposals} />
         <hr />
         <Link to={`/proposals`}>Show all</Link>
       </div>
-      <Council council={props.council} handles={props.handles} />
+      <Council
+        council={council}
+        handles={handles}
+        councilElection={props.councilElection}
+        block={block}
+      />
       <div className="d-flex flex-row">
-        <Validators validators={props.validators} handles={props.handles} />
-        <Nominators nominators={props.nominators} handles={props.handles} />
+        <Validators validators={props.validators} handles={handles} />
+        <Nominators nominators={props.nominators} handles={handles} />
       </div>
     </div>
   );

+ 1 - 0
src/types.ts

@@ -21,6 +21,7 @@ export interface IState {
   validators: string[];
   loading: boolean;
   council: Seat[];
+  councilElection?: { stage: any; round: number; termEndsAt: number };
   channels: number[];
   proposals: ProposalDetail[];
   posts: number[];