on efed151 help sign in
bridge/src/views/Senate.js
252 lines 7.1 kB history raw
1import React, { useCallback, useMemo, useState } from 'react';
2import cn from 'classnames';
3import { Grid, Text, Input, P, LinkButton } from 'indigo-react';
4import * as azimuth from 'azimuth-js';
5import ob from 'urbit-ob';
7import { useNetwork } from 'store/network';
8import { usePointCache } from 'store/pointCache';
9import { usePointCursor } from 'store/pointCursor';
11import * as need from 'lib/need';
12import useEthereumTransaction from 'lib/useEthereumTransaction';
13import { GAS_LIMITS } from 'lib/constants';
14import patp2dec from 'lib/patp2dec';
15import { getSpawnCandidate } from 'lib/child';
16import { useLocalRouter } from 'lib/LocalRouter';
17import useConstant from 'lib/useConstant';
19import { keccak256 } from 'lib/wallet';
21import ViewHeader from 'components/ViewHeader';
22import InlineEthereumTransaction from 'components/InlineEthereumTransaction';
23import View from 'components/View';
24import { PointInput, AddressInput } from 'form/Inputs';
25import {
26 composeValidator,
27 buildPointValidator,
28 buildAddressValidator,
29 hasErrors,
30} from 'form/validators';
31import BridgeForm from 'form/BridgeForm';
32import FormError from 'form/FormError';
33import CopiableAddress from 'components/CopiableAddress';
34import convertToInt from 'lib/convertToInt';
36function useCastDocumentVote() {
37 const { contracts } = useNetwork();
38 const { pointCursor } = usePointCursor();
39 const { syncDetails } = usePointCache();
41 const _contracts = need.contracts(contracts);
42 const _point = need.point(pointCursor);
44 return useEthereumTransaction(
45 useCallback(
46 (hash, accept) => {
47 return azimuth.ecliptic.castDocumentVote(
48 _contracts,
49 _point,
50 hash,
51 accept
52 );
53 },
54 [_contracts, _point]
55 ),
56 useCallback(() => null, []), //TODO update polls[hash].hasVoted ?
57 GAS_LIMITS.DEFAULT //TODO update once we have a decent sample size
58 );
61export default function Senate() {
62 const { pop } = useLocalRouter();
63 const { contracts } = useNetwork();
64 const { pointCursor } = usePointCursor();
66 const _contracts = need.contracts(contracts);
67 const _point = convertToInt(need.point(pointCursor), 10);
69 const {
70 construct,
71 unconstruct,
72 inputsLocked,
73 completed,
74 bind,
75 } = useCastDocumentVote();
77 const [documentHash, setDocumentHash] = useState('0x...');
78 const [majorities, setMajorities] = useState([]);
79 const [proposals, setProposals] = useState([]);
80 const [polls, setPolls] = useState({});
81 const [loading, setLoading] = useState(true);
82 const [votingOn, setVotingOn] = useState({});
84 //TODO update every second?
85 const now = Math.round(new Date().getTime() / 1000);
87 useMemo(async () => {
88 let proposals = await azimuth.polls.getDocumentProposals(_contracts);
89 let majorities = await azimuth.polls.getDocumentMajorities(_contracts);
90 let polls = {};
91 for (let doc of proposals) {
92 let poll = await azimuth.polls.getDocumentPoll(_contracts, doc);
93 poll.endTime = polls.start + poll.duration;
94 poll.hasVoted = await azimuth.polls.hasVotedOnDocumentPoll(
95 _contracts,
96 _point,
97 doc
98 );
99 polls[doc] = poll;
100 }
101 setMajorities(majorities);
102 setProposals(proposals);
103 setPolls(polls);
104 setLoading(false);
105 return;
106 }, [_contracts]);
108 const [open, closed] = useMemo(() => {
109 let open = [];
110 let closed = [];
111 if (loading) return [open, closed];
112 for (let doc of proposals) {
113 if (now > polls[doc].endTime) {
114 closed.push(doc);
115 } else {
116 open.push(doc);
117 }
118 }
119 return [open, closed];
120 }, [loading, proposals, polls, now]);
122 const doVote = useCallback(
123 (hash, accept) => {
124 construct(hash, accept);
125 setVotingOn({ hash, accept });
126 },
127 [construct, setVotingOn]
128 );
130 const didVote = useCallback(() => {
131 polls[votingOn].hasVoted = true;
132 setPolls(polls);
133 setVotingOn({});
134 }, [polls, setPolls, votingOn]);
136 const majorityList = useMemo(() => {
137 return majorities.map(doc => {
138 return (
139 <Grid.Item full as={Text}>
140 <a
141 target="_blank"
142 rel="noopener noreferrer"
143 href={`https://raw.githubusercontent.com/urbit/azimuth/master/proposals/${doc}.txt`}>
144 <code>{doc}</code>↗
145 </a>
146 </Grid.Item>
147 );
148 });
149 }, [majorities]);
151 const openList = useMemo(() => {
152 return open.map(doc => {
153 const hasVoted = polls[doc].hasVoted;
154 return (
155 <>
156 <Grid.Item full as={Text}>
157 <P>
158 <code>{doc}</code>
159 </P>
160 <P>
161 {hasVoted ? (
162 'Your vote has been cast.'
163 ) : (
164 <>
165 <LinkButton onClick={() => doVote(doc, true)}>
166 {votingOn.hash === doc && votingOn.accept ? (
167 <b>support</b>
168 ) : (
169 <>support</>
170 )}
171 </LinkButton>
172 {' / '}
173 <LinkButton onClick={() => doVote(doc, false)}>
174 {votingOn.hash === doc && !votingOn.accept ? (
175 <b>reject</b>
176 ) : (
177 <>reject</>
178 )}
179 </LinkButton>
180 </>
181 )}
182 </P>
183 </Grid.Item>
184 <Grid.Divider />
185 </>
186 );
187 });
188 }, [open, polls, votingOn]);
190 const onDocumentChange = useCallback(({ valid, values }) => {
191 if (values.document) {
192 const hash = '0x' + keccak256(values.document.trim()).toString('hex');
193 setDocumentHash(hash);
194 } else {
195 setDocumentHash('0x...');
196 }
197 }, []);
199 return (
200 <View pop={pop} inset>
201 <Grid>
202 <Grid.Item full as={ViewHeader}>
203 Senate: Document Proposals
204 </Grid.Item>
206 <Grid.Item full as={Grid}>
207 <Grid.Item full as={P}>
208 Accepted documents:
209 </Grid.Item>
210 {majorityList}
211 </Grid.Item>
213 <Grid.Divider />
215 <Grid.Item full as={Grid}>
216 <Grid.Item full as={P}>
217 Open document polls:
218 </Grid.Item>
219 {openList}
220 </Grid.Item>
222 <BridgeForm onValues={onDocumentChange}>
223 {({ onSubmit, values }) => (
224 // <>
225 <Grid.Item
226 full
227 as={InlineEthereumTransaction}
228 {...bind}
229 onReturn={didVote}
230 />
232 // <Grid.Divider />
233 //
234 // <Grid.Item
235 // full
236 // as={Input}
237 // type="textarea"
238 // placeholder="Input some text to find its hash..."
239 // name="document"
240 // className="mt4"
241 // />
242 //
243 // <Grid.Item full as={CopiableAddress}>
244 // {documentHash}
245 // </Grid.Item>
246 // </>
247 )}
248 </BridgeForm>
249 </Grid>
250 </View>
251 );