on ebac710 help sign in
bridge/src/views/Senate.js
232 lines 6.7 kB history raw
1import React, { useCallback, useMemo, useState } from 'react';
2import { Grid, Text, P, LinkButton } from 'indigo-react';
3import * as azimuth from 'azimuth-js';
5import { useNetwork } from 'store/network';
6import { usePointCursor } from 'store/pointCursor';
8import * as need from 'lib/need';
9import useEthereumTransaction from 'lib/useEthereumTransaction';
10import { GAS_LIMITS } from 'lib/constants';
11import { useLocalRouter } from 'lib/LocalRouter';
13import ViewHeader from 'components/ViewHeader';
14import InlineEthereumTransaction from 'components/InlineEthereumTransaction';
15import View from 'components/View';
16import BridgeForm from 'form/BridgeForm';
17import convertToInt from 'lib/convertToInt';
19function useCastDocumentVote() {
20 const { contracts } = useNetwork();
21 const { pointCursor } = usePointCursor();
23 const _contracts = need.contracts(contracts);
24 const _point = need.point(pointCursor);
26 return useEthereumTransaction(
27 useCallback(
28 (hash, accept) => {
29 return azimuth.ecliptic.castDocumentVote(
30 _contracts,
31 _point,
32 hash,
33 accept
34 );
35 },
36 [_contracts, _point]
37 ),
38 useCallback(() => null, []), //TODO update polls[hash].hasVoted ?
39 GAS_LIMITS.DEFAULT //TODO update once we have a decent sample size
40 );
43function renderDocumentHash(doc) {
44 return doc.slice(0, 34) + '\n ' + doc.slice(34);
47export default function Senate() {
48 const { pop } = useLocalRouter();
49 const { contracts } = useNetwork();
50 const { pointCursor } = usePointCursor();
52 const _contracts = need.contracts(contracts);
53 const _point = convertToInt(need.point(pointCursor), 10);
55 const { construct, bind } = useCastDocumentVote();
57 // const [documentHash, setDocumentHash] = useState('0x...');
58 const [majorities, setMajorities] = useState([]);
59 const [proposals, setProposals] = useState([]);
60 const [polls, setPolls] = useState({});
61 const [loading, setLoading] = useState(true);
62 const [votingOn, setVotingOn] = useState({});
64 //TODO update every second?
65 const now = Math.round(new Date().getTime() / 1000);
67 useMemo(async () => {
68 let proposals = await azimuth.polls.getDocumentProposals(_contracts);
69 let majorities = await azimuth.polls.getDocumentMajorities(_contracts);
70 let polls = {};
71 for (let doc of proposals) {
72 let poll = await azimuth.polls.getDocumentPoll(_contracts, doc);
73 poll.endTime = convertToInt(poll.start) + convertToInt(poll.duration);
74 poll.hasVoted = await azimuth.polls.hasVotedOnDocumentPoll(
75 _contracts,
76 _point,
77 doc
78 );
79 polls[doc] = poll;
80 }
81 setMajorities(majorities);
82 setProposals(proposals);
83 setPolls(polls);
84 setLoading(false);
85 return;
86 }, [_contracts, _point]);
88 // eslint-disable-next-line
89 const [open, closed] = useMemo(() => {
90 let open = [];
91 let closed = [];
92 if (loading) return [open, closed];
93 for (let doc of proposals) {
94 if (now > polls[doc].endTime) {
95 closed.push(doc);
96 } else {
97 open.push(doc);
98 }
99 }
100 return [open, closed];
101 }, [loading, proposals, polls, now]);
103 const doVote = useCallback(
104 (hash, accept) => {
105 construct(hash, accept);
106 setVotingOn({ hash, accept });
107 },
108 [construct, setVotingOn]
109 );
111 const didVote = useCallback(() => {
112 polls[votingOn].hasVoted = true;
113 setPolls(polls);
114 setVotingOn({});
115 }, [polls, setPolls, votingOn]);
117 const majorityList = useMemo(() => {
118 return majorities.map(doc => {
119 return (
120 <Grid.Item full className="mv2" as={P}>
121 <a
122 target="_blank"
123 rel="noopener noreferrer"
124 href={`https://raw.githubusercontent.com/urbit/azimuth/master/proposals/${doc}.txt`}
125 class="mb4 mono wrap pre">
126 {renderDocumentHash(doc)}↗
127 </a>
128 </Grid.Item>
129 );
130 });
131 }, [majorities]);
133 const openList = useMemo(() => {
134 return open.map(doc => {
135 const hasVoted = polls[doc].hasVoted;
136 return (
137 <>
138 <Grid.Item full className="mv2" as={Text}>
139 <P className="m0 mono wrap pre">{renderDocumentHash(doc)}</P>
140 <P className="mt0">
141 {hasVoted ? (
142 'Your vote has been cast.'
143 ) : (
144 <>
145 <LinkButton onClick={() => doVote(doc, true)}>
146 {votingOn.hash === doc && votingOn.accept ? (
147 <b>support</b>
148 ) : (
149 <>support</>
150 )}
151 </LinkButton>
152 {' / '}
153 <LinkButton onClick={() => doVote(doc, false)}>
154 {votingOn.hash === doc && !votingOn.accept ? (
155 <b>reject</b>
156 ) : (
157 <>reject</>
158 )}
159 </LinkButton>
160 </>
161 )}
162 </P>
163 </Grid.Item>
164 <Grid.Divider />
165 </>
166 );
167 });
168 }, [open, polls, votingOn, doVote]);
170 const onDocumentChange = useCallback(({ valid, values }) => {
171 // if (values.document) {
172 // const hash = '0x' + keccak256(values.document.trim()).toString('hex');
173 // setDocumentHash(hash);
174 // } else {
175 // setDocumentHash('0x...');
176 // }
177 }, []);
179 return (
180 <View pop={pop} inset>
181 <Grid>
182 <Grid.Item full as={ViewHeader}>
183 Senate: Document Proposals
184 </Grid.Item>
186 <Grid.Item full as={Grid}>
187 <Grid.Item full as={P}>
188 Accepted documents:
189 </Grid.Item>
190 {majorityList}
191 </Grid.Item>
193 <Grid.Divider className="mv4" />
195 <Grid.Item full as={Grid}>
196 <Grid.Item full as={P}>
197 Open document polls:
198 </Grid.Item>
199 {openList}
200 </Grid.Item>
202 <BridgeForm onValues={onDocumentChange}>
203 {({ onSubmit, values }) => (
204 // <>
205 <Grid.Item
206 full
207 as={InlineEthereumTransaction}
208 {...bind}
209 onReturn={didVote}
210 />
212 // <Grid.Divider />
213 //
214 // <Grid.Item
215 // full
216 // as={Input}
217 // type="textarea"
218 // placeholder="Input some text to find its hash..."
219 // name="document"
220 // className="mt4"
221 // />
222 //
223 // <Grid.Item full as={CopiableAddress}>
224 // {documentHash}
225 // </Grid.Item>
226 // </>
227 )}
228 </BridgeForm>
229 </Grid>
230 </View>
231 );