on fe6ee13 help sign in
bridge/src/views/Senate.js
230 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 { keccak256 } from 'lib/wallet';
15import ViewHeader from 'components/ViewHeader';
16import InlineEthereumTransaction from 'components/InlineEthereumTransaction';
17import View from 'components/View';
18import BridgeForm from 'form/BridgeForm';
19import convertToInt from 'lib/convertToInt';
21function useCastDocumentVote() {
22 const { contracts } = useNetwork();
23 const { pointCursor } = usePointCursor();
25 const _contracts = need.contracts(contracts);
26 const _point = need.point(pointCursor);
28 return useEthereumTransaction(
29 useCallback(
30 (hash, accept) => {
31 return azimuth.ecliptic.castDocumentVote(
32 _contracts,
33 _point,
34 hash,
35 accept
36 );
37 },
38 [_contracts, _point]
39 ),
40 useCallback(() => null, []), //TODO update polls[hash].hasVoted ?
41 GAS_LIMITS.DEFAULT //TODO update once we have a decent sample size
42 );
45export default function Senate() {
46 const { pop } = useLocalRouter();
47 const { contracts } = useNetwork();
48 const { pointCursor } = usePointCursor();
50 const _contracts = need.contracts(contracts);
51 const _point = convertToInt(need.point(pointCursor), 10);
53 const { construct, bind } = useCastDocumentVote();
55 // const [documentHash, setDocumentHash] = useState('0x...');
56 const [majorities, setMajorities] = useState([]);
57 const [proposals, setProposals] = useState([]);
58 const [polls, setPolls] = useState({});
59 const [loading, setLoading] = useState(true);
60 const [votingOn, setVotingOn] = useState({});
62 //TODO update every second?
63 const now = Math.round(new Date().getTime() / 1000);
65 useMemo(async () => {
66 let proposals = await azimuth.polls.getDocumentProposals(_contracts);
67 let majorities = await azimuth.polls.getDocumentMajorities(_contracts);
68 let polls = {};
69 for (let doc of proposals) {
70 let poll = await azimuth.polls.getDocumentPoll(_contracts, doc);
71 poll.endTime = convertToInt(poll.start) + convertToInt(poll.duration);
72 poll.hasVoted = await azimuth.polls.hasVotedOnDocumentPoll(
73 _contracts,
74 _point,
75 doc
76 );
77 polls[doc] = poll;
78 }
79 setMajorities(majorities);
80 setProposals(proposals);
81 setPolls(polls);
82 setLoading(false);
83 return;
84 }, [_contracts, _point]);
86 const [open, closed] = useMemo(() => {
87 let open = [];
88 let closed = [];
89 if (loading) return [open, closed];
90 for (let doc of proposals) {
91 if (now > polls[doc].endTime) {
92 closed.push(doc);
93 } else {
94 open.push(doc);
95 }
96 }
97 return [open, closed];
98 }, [loading, proposals, polls, now]);
100 const doVote = useCallback(
101 (hash, accept) => {
102 construct(hash, accept);
103 setVotingOn({ hash, accept });
104 },
105 [construct, setVotingOn]
106 );
108 const didVote = useCallback(() => {
109 polls[votingOn].hasVoted = true;
110 setPolls(polls);
111 setVotingOn({});
112 }, [polls, setPolls, votingOn]);
114 const majorityList = useMemo(() => {
115 return majorities.map(doc => {
116 return (
117 <Grid.Item full as={Text}>
118 <a
119 target="_blank"
120 rel="noopener noreferrer"
121 href={`https://raw.githubusercontent.com/urbit/azimuth/master/proposals/${doc}.txt`}>
122 <code>{doc}</code>↗
123 </a>
124 </Grid.Item>
125 );
126 });
127 }, [majorities]);
129 const openList = useMemo(() => {
130 return open.map(doc => {
131 const hasVoted = polls[doc].hasVoted;
132 return (
133 <>
134 <Grid.Item full as={Text}>
135 <P>
136 <code>{doc}</code>
137 </P>
138 <P>
139 {hasVoted ? (
140 'Your vote has been cast.'
141 ) : (
142 <>
143 <LinkButton onClick={() => doVote(doc, true)}>
144 {votingOn.hash === doc && votingOn.accept ? (
145 <b>support</b>
146 ) : (
147 <>support</>
148 )}
149 </LinkButton>
150 {' / '}
151 <LinkButton onClick={() => doVote(doc, false)}>
152 {votingOn.hash === doc && !votingOn.accept ? (
153 <b>reject</b>
154 ) : (
155 <>reject</>
156 )}
157 </LinkButton>
158 </>
159 )}
160 </P>
161 </Grid.Item>
162 <Grid.Divider />
163 </>
164 );
165 });
166 }, [open, polls, votingOn, doVote]);
168 const onDocumentChange = useCallback(({ valid, values }) => {
169 // if (values.document) {
170 // const hash = '0x' + keccak256(values.document.trim()).toString('hex');
171 // setDocumentHash(hash);
172 // } else {
173 // setDocumentHash('0x...');
174 // }
175 }, []);
177 return (
178 <View pop={pop} inset>
179 <Grid>
180 <Grid.Item full as={ViewHeader}>
181 Senate: Document Proposals
182 </Grid.Item>
184 <Grid.Item full as={Grid}>
185 <Grid.Item full as={P}>
186 Accepted documents:
187 </Grid.Item>
188 {majorityList}
189 </Grid.Item>
191 <Grid.Divider />
193 <Grid.Item full as={Grid}>
194 <Grid.Item full as={P}>
195 Open document polls:
196 </Grid.Item>
197 {openList}
198 </Grid.Item>
200 <BridgeForm onValues={onDocumentChange}>
201 {({ onSubmit, values }) => (
202 // <>
203 <Grid.Item
204 full
205 as={InlineEthereumTransaction}
206 {...bind}
207 onReturn={didVote}
208 />
210 // <Grid.Divider />
211 //
212 // <Grid.Item
213 // full
214 // as={Input}
215 // type="textarea"
216 // placeholder="Input some text to find its hash..."
217 // name="document"
218 // className="mt4"
219 // />
220 //
221 // <Grid.Item full as={CopiableAddress}>
222 // {documentHash}
223 // </Grid.Item>
224 // </>
225 )}
226 </BridgeForm>
227 </Grid>
228 </View>
229 );