@@ -0,0 +1,252 @@
1+import React, { useCallback, useMemo, useState } from 'react';
2+import cn from 'classnames';
3+import { Grid, Text, Input, P, LinkButton } from 'indigo-react';
4+import * as azimuth from 'azimuth-js';
5+import ob from 'urbit-ob';
7+import { useNetwork } from 'store/network';
8+import { usePointCache } from 'store/pointCache';
9+import { usePointCursor } from 'store/pointCursor';
11+import * as need from 'lib/need';
12+import useEthereumTransaction from 'lib/useEthereumTransaction';
13+import { GAS_LIMITS } from 'lib/constants';
14+import patp2dec from 'lib/patp2dec';
15+import { getSpawnCandidate } from 'lib/child';
16+import { useLocalRouter } from 'lib/LocalRouter';
17+import useConstant from 'lib/useConstant';
19+import { keccak256 } from 'lib/wallet';
21+import ViewHeader from 'components/ViewHeader';
22+import InlineEthereumTransaction from 'components/InlineEthereumTransaction';
23+import View from 'components/View';
24+import { PointInput, AddressInput } from 'form/Inputs';
28+ buildAddressValidator,
30+} from 'form/validators';
31+import BridgeForm from 'form/BridgeForm';
32+import FormError from 'form/FormError';
33+import CopiableAddress from 'components/CopiableAddress';
34+import convertToInt from 'lib/convertToInt';
36+function 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(
47+ return azimuth.ecliptic.castDocumentVote(
56+ useCallback(() => null, []), //TODO update polls[hash].hasVoted ?
57+ GAS_LIMITS.DEFAULT //TODO update once we have a decent sample size
61+export 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);
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);
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(
101+ setMajorities(majorities);
102+ setProposals(proposals);
108+ const [open, closed] = useMemo(() => {
111+ if (loading) return [open, closed];
112+ for (let doc of proposals) {
113+ if (now > polls[doc].endTime) {
119+ return [open, closed];
120+ }, [loading, proposals, polls, now]);
122+ const doVote = useCallback(
124+ construct(hash, accept);
125+ setVotingOn({ hash, accept });
127+ [construct, setVotingOn]
130+ const didVote = useCallback(() => {
131+ polls[votingOn].hasVoted = true;
134+ }, [polls, setPolls, votingOn]);
136+ const majorityList = useMemo(() => {
137+ return majorities.map(doc => {
139+ <Grid.Item full as={Text}>
142+ rel="noopener noreferrer"
143+ href={`https://github.com/urbit/azimuth/blob/master/proposals/${doc}.txt`}>
151+ const openList = useMemo(() => {
152+ return open.map(doc => {
153+ const hasVoted = polls[doc].hasVoted;
156+ <Grid.Item full as={Text}>
162+ 'Your vote has been cast.'
165+ <LinkButton onClick={() => doVote(doc, true)}>
166+ {votingOn.hash === doc && votingOn.accept ? (
173+ <LinkButton onClick={() => doVote(doc, false)}>
174+ {votingOn.hash === doc && !votingOn.accept ? (
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);
195+ setDocumentHash('0x...');
200+ <View pop={pop} inset>
202+ <Grid.Item full as={ViewHeader}>
203+ Senate: Document Proposals
206+ <Grid.Item full as={Grid}>
207+ <Grid.Item full as={P}>
215+ <Grid.Item full as={Grid}>
216+ <Grid.Item full as={P}>
217+ Open document polls:
222+ <BridgeForm onValues={onDocumentChange}>
223+ {({ onSubmit, values }) => (
227+ as={InlineEthereumTransaction}
238+ // placeholder="Input some text to find its hash..."
243+ // <Grid.Item full as={CopiableAddress}>