on 8d566ea help sign in
bridge/src/views/Senate.js
229 lines 6.5 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 );
43export default function Senate() {
44 const { pop } = useLocalRouter();
45 const { contracts } = useNetwork();
46 const { pointCursor } = usePointCursor();
48 const _contracts = need.contracts(contracts);
49 const _point = convertToInt(need.point(pointCursor), 10);
51 const { construct, bind } = useCastDocumentVote();
53 // const [documentHash, setDocumentHash] = useState('0x...');
54 const [majorities, setMajorities] = useState([]);
55 const [proposals, setProposals] = useState([]);
56 const [polls, setPolls] = useState({});
57 const [loading, setLoading] = useState(true);
58 const [votingOn, setVotingOn] = useState({});
60 //TODO update every second?
61 const now = Math.round(new Date().getTime() / 1000);
63 useMemo(async () => {
64 let proposals = await azimuth.polls.getDocumentProposals(_contracts);
65 let majorities = await azimuth.polls.getDocumentMajorities(_contracts);
66 let polls = {};
67 for (let doc of proposals) {
68 let poll = await azimuth.polls.getDocumentPoll(_contracts, doc);
69 poll.endTime = convertToInt(poll.start) + convertToInt(poll.duration);
70 poll.hasVoted = await azimuth.polls.hasVotedOnDocumentPoll(
71 _contracts,
72 _point,
73 doc
74 );
75 polls[doc] = poll;
76 }
77 setMajorities(majorities);
78 setProposals(proposals);
79 setPolls(polls);
80 setLoading(false);
81 return;
82 }, [_contracts, _point]);
84 // eslint-disable-next-line
85 const [open, closed] = useMemo(() => {
86 let open = [];
87 let closed = [];
88 if (loading) return [open, closed];
89 for (let doc of proposals) {
90 if (now > polls[doc].endTime) {
91 closed.push(doc);
92 } else {
93 open.push(doc);
94 }
95 }
96 return [open, closed];
97 }, [loading, proposals, polls, now]);
99 const doVote = useCallback(
100 (hash, accept) => {
101 construct(hash, accept);
102 setVotingOn({ hash, accept });
103 },
104 [construct, setVotingOn]
105 );
107 const didVote = useCallback(() => {
108 polls[votingOn].hasVoted = true;
109 setPolls(polls);
110 setVotingOn({});
111 }, [polls, setPolls, votingOn]);
113 const majorityList = useMemo(() => {
114 return majorities.map(doc => {
115 return (
116 <Grid.Item full as={Text}>
117 <a
118 target="_blank"
119 rel="noopener noreferrer"
120 href={`https://raw.githubusercontent.com/urbit/azimuth/master/proposals/${doc}.txt`}>
121 <code>{doc}</code>↗
122 </a>
123 </Grid.Item>
124 );
125 });
126 }, [majorities]);
128 const openList = useMemo(() => {
129 return open.map(doc => {
130 const hasVoted = polls[doc].hasVoted;
131 return (
132 <>
133 <Grid.Item full as={Text}>
134 <P>
135 <code>{doc}</code>
136 </P>
137 <P>
138 {hasVoted ? (
139 'Your vote has been cast.'
140 ) : (
141 <>
142 <LinkButton onClick={() => doVote(doc, true)}>
143 {votingOn.hash === doc && votingOn.accept ? (
144 <b>support</b>
145 ) : (
146 <>support</>
147 )}
148 </LinkButton>
149 {' / '}
150 <LinkButton onClick={() => doVote(doc, false)}>
151 {votingOn.hash === doc && !votingOn.accept ? (
152 <b>reject</b>
153 ) : (
154 <>reject</>
155 )}
156 </LinkButton>
157 </>
158 )}
159 </P>
160 </Grid.Item>
161 <Grid.Divider />
162 </>
163 );
164 });
165 }, [open, polls, votingOn, doVote]);
167 const onDocumentChange = useCallback(({ valid, values }) => {
168 // if (values.document) {
169 // const hash = '0x' + keccak256(values.document.trim()).toString('hex');
170 // setDocumentHash(hash);
171 // } else {
172 // setDocumentHash('0x...');
173 // }
174 }, []);
176 return (
177 <View pop={pop} inset>
178 <Grid>
179 <Grid.Item full as={ViewHeader}>
180 Senate: Document Proposals
181 </Grid.Item>
183 <Grid.Item full as={Grid}>
184 <Grid.Item full as={P}>
185 Accepted documents:
186 </Grid.Item>
187 {majorityList}
188 </Grid.Item>
190 <Grid.Divider />
192 <Grid.Item full as={Grid}>
193 <Grid.Item full as={P}>
194 Open document polls:
195 </Grid.Item>
196 {openList}
197 </Grid.Item>
199 <BridgeForm onValues={onDocumentChange}>
200 {({ onSubmit, values }) => (
201 // <>
202 <Grid.Item
203 full
204 as={InlineEthereumTransaction}
205 {...bind}
206 onReturn={didVote}
207 />
209 // <Grid.Divider />
210 //
211 // <Grid.Item
212 // full
213 // as={Input}
214 // type="textarea"
215 // placeholder="Input some text to find its hash..."
216 // name="document"
217 // className="mt4"
218 // />
219 //
220 // <Grid.Item full as={CopiableAddress}>
221 // {documentHash}
222 // </Grid.Item>
223 // </>
224 )}
225 </BridgeForm>
226 </Grid>
227 </View>
228 );