Browse Source

Councils refactor

Joystream Stats 3 years ago
parent
commit
e7ab1fcc2a

+ 3 - 9
src/App.tsx

@@ -340,18 +340,12 @@ class App extends React.Component<IProps, IState> {
     let { proposals } = this.state;
     const exists = proposals.find((p) => p && p.id === id);
 
-    if (
-      exists &&
-      exists.stage === "Finalized" &&
-      exists.votesByAccount &&
-      exists.votesByAccount.length
-    )
-      return;
+    if (exists && exists.stage === "Finalized")
+      if (exists.votesByAccount && exists.votesByAccount.length) return;
+      else return this.fetchVotesPerProposal(api, exists);
 
     console.debug(`Fetching proposal ${id}`);
     const proposal = await get.proposalDetail(api, id);
-    if (!proposal) return console.warn(`Empty result (proposal ${id})`);
-
     proposals[id] = proposal;
     this.save("proposals", proposals);
     this.fetchVotesPerProposal(api, proposal);

+ 97 - 0
src/components/Councils/CouncilVotes.tsx

@@ -0,0 +1,97 @@
+import React, { Component } from "react";
+import { Table } from "react-bootstrap";
+import ProposalOverlay from "../Proposals/ProposalOverlay";
+import VoteDiv from "./VoteDiv";
+import { Member, ProposalDetail, Seat } from "../../types";
+
+interface IProps {
+  block: number;
+  round: number;
+  council: Seat[];
+  members: Member[];
+  proposals: ProposalDetail[];
+  expand?: boolean;
+}
+interface IState {
+  expand: boolean;
+}
+
+class CouncilVotes extends Component<IProps, IState> {
+  constructor(props: IProps) {
+    super(props);
+    this.state = { expand: false };
+    this.toggleExpand = this.toggleExpand.bind(this);
+  }
+
+  componentDidMount() {
+    const { expand } = this.props;
+    if (expand) this.setState({ expand });
+  }
+
+  toggleExpand() {
+    this.setState({ expand: !this.state.expand });
+  }
+
+  render() {
+    const { block, council, members, proposals, round } = this.props;
+    const { expand } = this.state;
+
+    let councilMembers: Member[] = [];
+    council.forEach((seat) => {
+      const member = members.find((m) => m.account === seat.member);
+      if (!member) return;
+      councilMembers.push(member);
+    });
+
+    const fail = "btn btn-outline-danger";
+    const styles: { [key: string]: string } = {
+      Approved: "btn btn-success",
+      Rejected: fail,
+      Canceled: fail,
+      Expired: fail,
+      Pending: "btn btn-warning",
+    };
+
+    return (
+      <Table className="text-light text-center">
+        <thead onClick={this.toggleExpand}>
+          <tr>
+            <th className="text-right" style={{ width: "40%" }}>
+              Round {round}
+            </th>
+            {councilMembers.map((member) => (
+              <th key={member.handle} style={{ width: "10%" }}>
+                {member.handle}
+              </th>
+            ))}
+          </tr>
+        </thead>
+        <tbody>
+          {expand &&
+            proposals
+              .sort((a, b) => a.createdAt - b.createdAt)
+              .map((p) => (
+                <tr key={p.id} className={`text-left`}>
+                  <td
+                    className={`float-right mt-1 p-0 px-1 ${styles[p.result]}`}
+                  >
+                    <ProposalOverlay block={block} {...p} />
+                  </td>
+
+                  {p.votesByAccount &&
+                    council.map((seat) => (
+                      <VoteDiv
+                        key={seat.member}
+                        votes={p.votesByAccount}
+                        member={members.find((m) => m.account === seat.member)}
+                      />
+                    ))}
+                </tr>
+              ))}
+        </tbody>
+      </Table>
+    );
+  }
+}
+
+export default CouncilVotes;

+ 27 - 0
src/components/Councils/VoteDiv.tsx

@@ -0,0 +1,27 @@
+import React from "react";
+import { Button } from "react-bootstrap";
+import { Member, Vote } from "../../types";
+
+const VoteDiv = (props: { votes?: Vote[]; member?: Member }) => {
+  const { votes, member } = props;
+  if (!votes || !member) return <span />;
+
+  const v = votes.find((v) => v.handle === member.handle);
+  if (!v || v.vote === "") return <span />;
+
+  const styles: { [key: string]: string } = {
+    Approve: "success",
+    Reject: "danger",
+    Abstain: "dark",
+  };
+
+  return (
+    <td className="text-center p-0">
+      <Button className="p-0" variant={styles[v.vote]}>
+        {v.vote}
+      </Button>
+    </td>
+  );
+};
+
+export default VoteDiv;

+ 9 - 97
src/components/Councils/index.tsx

@@ -1,9 +1,9 @@
 import React from "react";
-import { OverlayTrigger, Tooltip, Table } from "react-bootstrap";
-import { Link } from "react-router-dom";
-import { Member, ProposalDetail, Seat, Vote } from "../../types";
+import { Table } from "react-bootstrap";
 import LeaderBoard from "./Leaderboard";
-import Back from "../Back";
+import CouncilVotes from "./CouncilVotes";
+
+import { Member, ProposalDetail, Seat } from "../../types";
 
 // TODO fetch from chain
 const announcingPeriod = 28800;
@@ -13,11 +13,12 @@ const termDuration = 144000;
 const cycle = termDuration + announcingPeriod + votingPeriod + revealingPeriod; // 201600
 
 const Rounds = (props: {
+  block: number;
   members: Member[];
   councils: Seat[][];
   proposals: any;
 }) => {
-  const { councils, members, proposals } = props;
+  const { block, councils, members, proposals } = props;
   return (
     <div className="w-100">
       <Table className="w-100 text-light">
@@ -52,10 +53,12 @@ const Rounds = (props: {
         proposals={proposals}
       />
 
-      <h2 className="w-100 text-center text-light">Votes per Proposal</h2>
+      <h2 className="w-100 text-center text-light">Votes per Council</h2>
       {councils.map((council, i: number) => (
         <CouncilVotes
           key={i}
+          expand={i === councils.length - 1}
+          block={block}
           round={i + 1}
           council={council}
           members={props.members}
@@ -71,95 +74,4 @@ const Rounds = (props: {
   );
 };
 
-const CouncilVotes = (props: {
-  round: number;
-  council: Seat[];
-  members: Member[];
-  proposals: ProposalDetail[];
-}) => {
-  const { council, members, proposals, round } = props;
-
-  let councilMembers: Member[] = [];
-  council.forEach((seat) => {
-    const member = members.find((m) => m.account === seat.member);
-    if (!member) return;
-    councilMembers.push(member);
-  });
-
-  const fail = "btn btn-outline-danger";
-  const styles: { [key: string]: string } = {
-    Approved: "btn btn-outline-success",
-    Rejected: fail,
-    Canceled: fail,
-    Expired: fail,
-    Pending: "btn btn-outline-warning",
-  };
-
-  return (
-    <Table className="text-light text-center">
-      <thead>
-        <tr>
-          <th className={`text-left`}>Round {round}</th>
-          {councilMembers.map((member) => (
-            <th key={member.handle}>{member.handle}</th>
-          ))}
-        </tr>
-      </thead>
-      <tbody>
-        {proposals
-          .sort((a, b) => a.createdAt - b.createdAt)
-          .map((p) => (
-            <tr key={p.id} className={`text-left`}>
-              <OverlayTrigger
-                placement="right"
-                overlay={
-                  <Tooltip id={`tooltip-${p.id}`}>
-                    <div>{p.result}</div>
-                  </Tooltip>
-                }
-              >
-                <td className={`text-left p-1 ${styles[p.result]}`}>
-                  <Link to={`/proposals/${p.id}`}>
-                    {p.title} ({p.id})
-                  </Link>
-                </td>
-              </OverlayTrigger>
-
-              {p.votesByAccount &&
-                council.map((seat) => (
-                  <td key={seat.member}>
-                    <VoteDiv
-                      votes={p.votesByAccount}
-                      member={members.find((m) => m.account === seat.member)}
-                    />
-                  </td>
-                ))}
-            </tr>
-          ))}
-        <tr>
-          <td className="text-center" colSpan={7}>
-            <Back />
-          </td>
-        </tr>
-      </tbody>
-    </Table>
-  );
-};
-
-const VoteDiv = (props: { votes?: Vote[]; member?: Member }) => {
-  const { votes, member } = props;
-  if (!votes || !member) return <span />;
-
-  const v = votes.find((v) => v.handle === member.handle);
-  if (!v) return <span />;
-
-  const styles: { [key: string]: string } = {
-    Approve: "btn btn-success",
-    Reject: "btn btn-outline-danger",
-    Abstain: "btn btn-outline-light",
-  };
-
-  return <div className={`text-center p-1 ${styles[v.vote]}`}>{v.vote}</div>;
-};
-
 export default Rounds;

+ 46 - 0
src/components/Forum/LatestPost.tsx

@@ -0,0 +1,46 @@
+import React from "react";
+import User from "../User";
+import Markdown from "react-markdown";
+import gfm from "remark-gfm";
+import moment from "moment";
+import { domain } from "../../config";
+
+import { Handles, Thread, Post } from "../../types";
+
+const LatestPost = (props: {
+  selectThread: (id: number) => void;
+  handles: Handles;
+  post: Post;
+  thread?: Thread;
+  startTime: number;
+}) => {
+  const { handles, thread, post, startTime } = props;
+  const { authorId, createdAt, id, threadId, text } = post;
+
+  return (
+    <div
+      key={id}
+      className="box d-flex flex-row"
+      onClick={() => thread && props.selectThread(thread.id)}
+    >
+      <div className="mr-3">
+        <User key={authorId} id={authorId} handle={handles[authorId]} />
+        <div>{moment(startTime + createdAt.block * 6000).fromNow()}</div>
+        <a href={`${domain}/#/forum/threads/${threadId}`}>reply</a>
+      </div>
+
+      <div>
+        <div className="text-left mb-3">
+          <b>{thread && thread.title}</b>
+        </div>
+        <Markdown
+          plugins={[gfm]}
+          className="overflow-auto text-left"
+          children={text.slice(0, 200) + `...`}
+        />
+      </div>
+    </div>
+  );
+};
+
+export default LatestPost;

+ 10 - 6
src/components/Proposals/ProposalOverlay.tsx

@@ -2,6 +2,7 @@ import React from "react";
 import { Link } from "react-router-dom";
 import { OverlayTrigger, Tooltip } from "react-bootstrap";
 import htmr from "htmr";
+
 import { ProposalParameters } from "@joystream/types/proposals";
 
 const ProposalOverlay = (props: {
@@ -12,8 +13,9 @@ const ProposalOverlay = (props: {
   title: string;
   message: string;
   description: string;
+  result: string;
 }) => {
-  const { block, createdAt, parameters } = props;
+  const { block, createdAt, message, parameters, result, title } = props;
 
   const remainingBlocks = +createdAt + +parameters.votingPeriod - block;
   const remainingTime = remainingBlocks * 6;
@@ -26,13 +28,15 @@ const ProposalOverlay = (props: {
       placement="right"
       delay={{ show: 250, hide: 400 }}
       overlay={
-        <Tooltip id={props.title}>
-          <div>
-            Time to vote: {remainingBlocks} blocks ({days}d {hours}h)
-          </div>
+        <Tooltip id={title}>
+          {result === "Pending" && (
+            <div>
+              Time to vote: {remainingBlocks} blocks ({days}d {hours}h)
+            </div>
+          )}
 
           <div className="my-2 p-1 bg-light  text-secondary text-left">
-            {props.message.split(/\n/).map((line: string, i: number) => (
+            {message.split(/\n/).map((line: string, i: number) => (
               <div key={i}>{htmr(line)}</div>
             ))}
           </div>