Society vouching (#2204)

* Society vouching

* Bump API, decode votes

* Inline count

* Remove unused style
This commit is contained in:
Jaco Greeff 2020-01-28 10:13:44 +01:00 committed by GitHub
parent b896cd35b3
commit 5cab16d1f0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 256 additions and 67 deletions

View File

@ -10,10 +10,10 @@
"packages/*"
],
"resolutions": {
"@polkadot/api": "^1.0.0-beta.25",
"@polkadot/api-contract": "^1.0.0-beta.25",
"@polkadot/api": "^1.0.0-beta.26",
"@polkadot/api-contract": "^1.0.0-beta.26",
"@polkadot/keyring": "^2.0.0-beta.6",
"@polkadot/types": "^1.0.0-beta.25",
"@polkadot/types": "^1.0.0-beta.26",
"@polkadot/util": "^2.0.0-beta.6",
"@polkadot/util-crypto": "^2.0.0-beta.6",
"babel-core": "^7.0.0-bridge.0",

View File

@ -11,6 +11,6 @@
"license": "Apache-2.0",
"dependencies": {
"@babel/runtime": "^7.8.3",
"@polkadot/api-contract": "^1.0.0-beta.25"
"@polkadot/api-contract": "^1.0.0-beta.26"
}
}

View File

@ -38,7 +38,7 @@ function Proposal ({ className, value: { balance, hash, index, proposal, propose
proposalHash={hash}
proposal={proposal}
/>
<td className='top seconding'>
<td className='top padtop'>
{seconding.length !== 0 && (
<details>
<summary>
@ -83,8 +83,4 @@ export default styled(Proposal)`
margin-bottom: 4px;
}
}
.seconding {
padding-top: 1.1rem;
}
`;

View File

@ -3,19 +3,53 @@
// of the Apache-2.0 license. See the LICENSE file for details.
import { DeriveSocietyCandidate } from '@polkadot/api-derive/types';
import { AccountId, SocietyVote } from '@polkadot/types/interfaces';
import React from 'react';
import { AddressSmall } from '@polkadot/react-components';
import React, { useEffect, useState } from 'react';
import { AddressMini, AddressSmall } from '@polkadot/react-components';
import { useApi, useCall } from '@polkadot/react-hooks';
import { FormatBalance } from '@polkadot/react-query';
import { Option } from '@polkadot/types';
import { useTranslation } from '../translate';
import CandidateVoting from './CandidateVoting';
interface Props {
allMembers: string[];
isMember: boolean;
ownMembers: string[];
value: DeriveSocietyCandidate;
}
export default function Candidate ({ value: { accountId, kind, value } }: Props): React.ReactElement<Props> {
type VoteType = [string, SocietyVote];
interface VoteSplit {
allAye: VoteType[];
allNay: VoteType[];
allSkeptic: VoteType[];
}
export default function Candidate ({ allMembers, isMember, ownMembers, value: { accountId, kind, value } }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const { api } = useApi();
const [{ allAye, allNay, allSkeptic }, setVoteSplit] = useState<VoteSplit>({ allAye: [], allNay: [], allSkeptic: [] });
const votes = useCall<VoteType[]>(api.query.society.votes.multi as any, [allMembers.map((memberId): [AccountId, string] => [accountId, memberId])] as any, {
transform: (voteOpts: Option<SocietyVote>[]): VoteType[] =>
voteOpts
.map((voteOpt, index): [string, Option<SocietyVote>] => [allMembers[index], voteOpt])
.filter(([, voteOpt]): boolean => voteOpt.isSome)
.map(([accountId, voteOpt]): VoteType => [accountId, voteOpt.unwrap()])
});
useEffect((): void => {
if (votes) {
setVoteSplit({
allAye: votes.filter(([, vote]): boolean => vote.isApprove),
allNay: votes.filter(([, vote]): boolean => vote.isReject),
allSkeptic: votes.filter(([, vote]): boolean => vote.isSkeptic)
});
}
}, [votes]);
return (
<tr>
@ -32,6 +66,58 @@ export default function Candidate ({ value: { accountId, kind, value } }: Props)
value={value}
/>
</td>
<td className='top padtop'>
{allSkeptic.length !== 0 && (
<details>
<summary>
{t('Skeptics ({{count}})', { replace: { count: allSkeptic.length } })}
</summary>
{allSkeptic.map(([who]): React.ReactNode =>
<AddressMini
key={who.toString()}
value={who}
/>
)}
</details>
)}
</td>
<td className='top padtop'>
{allAye.length !== 0 && (
<details>
<summary>
{t('Approvals ({{count}})', { replace: { count: allAye.length } })}
</summary>
{allAye.map(([who]): React.ReactNode =>
<AddressMini
key={who.toString()}
value={who}
/>
)}
</details>
)}
</td>
<td className='top padtop'>
{allNay.length !== 0 && (
<details>
<summary>
{t('Rejections ({{count}})', { replace: { count: allNay.length } })}
</summary>
{allNay.map(([who]): React.ReactNode =>
<AddressMini
key={who.toString()}
value={who}
/>
)}
</details>
)}
</td>
<td className='number together top'>
<CandidateVoting
candidateId={accountId.toString()}
isMember={isMember}
ownMembers={ownMembers}
/>
</td>
</tr>
);
}

View File

@ -0,0 +1,67 @@
// Copyright 2017-2020 @polkadot/app-society authors & contributors
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import React, { useMemo, useState } from 'react';
import { useToggle } from '@polkadot/react-hooks';
import { useTranslation } from '../translate';
import { Button, Dropdown, InputAddress, Modal, TxButton } from '@polkadot/react-components';
interface Props {
candidateId: string;
isMember: boolean;
ownMembers: string[];
}
export default function CandidateVoting ({ candidateId, isMember, ownMembers }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const [isVisible, toggleVisible] = useToggle();
const [vote, setVote] = useState(true);
const [accountId, setAccountId] = useState<string | null>(null);
const voteOpts = useMemo(() => [
{ text: t('Aye, I approve'), value: true },
{ text: t('Nay, I do not approve'), value: false }
], [t]);
return (
<>
{isVisible && (
<Modal header={t('Vote for candidate')}>
<Modal.Content>
<InputAddress
filter={ownMembers}
help={t('The address to vote from (must be a member)')}
label={t('vote from account')}
onChange={setAccountId}
/>
<Dropdown
help={t('Approve this candidacy.')}
label={t('vote for andidate')}
onChange={setVote}
options={voteOpts}
value={vote}
/>
</Modal.Content>
<Modal.Actions onCancel={toggleVisible}>
<TxButton
accountId={accountId}
icon='check'
label={t('Vote')}
onStart={toggleVisible}
params={[candidateId, vote]}
tx='society.vote'
/>
</Modal.Actions>
</Modal>
)}
<Button
icon='check'
isDisabled={!isMember}
isPrimary
label={t('Vote')}
onClick={toggleVisible}
/>
</>
);
}

View File

@ -9,6 +9,7 @@ import { Table } from '@polkadot/react-components';
import { useApi, useCall } from '@polkadot/react-hooks';
import { useTranslation } from '../translate';
import useMembers from '../useMembers';
import Candidate from './Candidate';
interface Props {
@ -19,6 +20,7 @@ export default function Candidates ({ className }: Props): React.ReactElement<Pr
const { t } = useTranslation();
const { api } = useApi();
const candidates = useCall<DeriveSocietyCandidate[]>(api.derive.society.candidates, []);
const { allMembers, isMember, ownMembers } = useMembers();
return (
<div className={`overviewSection ${className}`}>
@ -29,7 +31,10 @@ export default function Candidates ({ className }: Props): React.ReactElement<Pr
<Table.Body>
{candidates.map((candidate): React.ReactNode => (
<Candidate
allMembers={allMembers}
isMember={isMember}
key={candidate.accountId.toString()}
ownMembers={ownMembers}
value={candidate}
/>
))}

View File

@ -0,0 +1,35 @@
// Copyright 2017-2020 @polkadot/app-society authors & contributors
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import { DeriveSocietyMember } from '@polkadot/api-derive/types';
import { useEffect, useState } from 'react';
import { useAccounts, useApi, useCall } from '@polkadot/react-hooks';
interface OwnMembers {
allMembers: string[];
isMember: boolean;
ownMembers: string[];
}
export default function useMembers (): OwnMembers {
const { api } = useApi();
const { allAccounts } = useAccounts();
const members = useCall<DeriveSocietyMember[]>(api.derive.society.members, []);
const [ownState, setOwnState] = useState<OwnMembers>({ allMembers: [], isMember: false, ownMembers: [] });
useEffect((): void => {
if (allAccounts && members) {
const allMembers = members
.filter((member): boolean => !member.isSuspended)
.map((member): string => member.accountId.toString());
const ownMembers = allMembers
.filter((address): boolean => allAccounts.includes(address));
setOwnState({ allMembers, isMember: ownMembers.length !== 0, ownMembers });
}
}, [allAccounts, members]);
return ownState;
}

View File

@ -31,7 +31,7 @@
"homepage": "https://github.com/polkadot-js/ui/tree/master/packages/ui-reactive#readme",
"dependencies": {
"@babel/runtime": "^7.8.3",
"@polkadot/api": "^1.0.0-beta.25",
"@polkadot/api": "^1.0.0-beta.26",
"@polkadot/extension-dapp": "^0.20.1",
"edgeware-node-types": "^1.1.0",
"rxjs-compat": "^6.5.3"

View File

@ -135,9 +135,8 @@ export default styled(Table)`
}
}
&.toppad {
padding-top: 1.25rem;
vertical-align: top;
&.padtop {
padding-top: 1.1rem;
}
}

View File

@ -2,7 +2,7 @@
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import React from 'react';
import React, { useMemo } from 'react';
import Dropdown from './Dropdown';
import { useTranslation } from './translate';
@ -15,16 +15,17 @@ interface Props {
export default function VoteToggle ({ className, onChange, value }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const voteOpts = useMemo(() => [
{ text: t('Aye, I approve'), value: true },
{ text: t('Nay, I do not approve'), value: false }
], [t]);
return (
<Dropdown
className={className}
help={t('Select your vote preferences for this proposal, either to approve or disapprove')}
label={t('record my vote as')}
options={[
{ text: t('Aye, I approve'), value: true },
{ text: t('Nay, I do not approve'), value: false }
]}
options={voteOpts}
onChange={onChange}
value={value}
/>

View File

@ -2196,35 +2196,35 @@
dependencies:
"@types/node" ">= 8"
"@polkadot/api-contract@^1.0.0-beta.25":
version "1.0.0-beta.25"
resolved "https://registry.yarnpkg.com/@polkadot/api-contract/-/api-contract-1.0.0-beta.25.tgz#af65e9305d136224d249e0b7f7d8dfdee27233b1"
integrity sha512-WG4aSmyZ0VdV64R7vBTQfyHTkYqm5hSPlYs2TXOyKcPTeQxlzcgSCaWlGeXqWkH5fY0dKrBNVYofV9uA8ymjew==
"@polkadot/api-contract@^1.0.0-beta.26":
version "1.0.0-beta.26"
resolved "https://registry.yarnpkg.com/@polkadot/api-contract/-/api-contract-1.0.0-beta.26.tgz#a44660412e2020941e00b55cc78e79569e16b895"
integrity sha512-BBX3WeL0ZZYUcNTroYTZ++cKV56Ok8E4TdfhlqhOIhUiXOIlC1/Ie+D02r1NuWGOhFJNk8fBXA1lqJE9gvtr8A==
dependencies:
"@babel/runtime" "^7.8.3"
"@polkadot/types" "^1.0.0-beta.25"
"@polkadot/types" "^1.0.0-beta.26"
"@polkadot/api-derive@^1.0.0-beta.25":
version "1.0.0-beta.25"
resolved "https://registry.yarnpkg.com/@polkadot/api-derive/-/api-derive-1.0.0-beta.25.tgz#910b0c53516ec54234db72cc04961643eab8815d"
integrity sha512-wZW5WOzpK7NeFLdMzb5HlAHDJwmQ3CePlYrsR4eZyom2l3AfkzBw3Mxf1oltsKN3t563r98Lc0u0cGwEZOhYoQ==
"@polkadot/api-derive@^1.0.0-beta.26":
version "1.0.0-beta.26"
resolved "https://registry.yarnpkg.com/@polkadot/api-derive/-/api-derive-1.0.0-beta.26.tgz#74a1f5f0a456c9cf417687273423de05fa4a11ba"
integrity sha512-SzpkvAA+bICv42Zk3uuBS3nPFL/gdSJQveydvTqzrvvi2ztlawfryeDiaEA1gND6E1JVJXgzGLDnkSbqfdSTaw==
dependencies:
"@babel/runtime" "^7.8.3"
"@polkadot/api" "^1.0.0-beta.25"
"@polkadot/types" "^1.0.0-beta.25"
"@polkadot/api" "^1.0.0-beta.26"
"@polkadot/types" "^1.0.0-beta.26"
"@polkadot/api@^1.0.0-beta.25":
version "1.0.0-beta.25"
resolved "https://registry.yarnpkg.com/@polkadot/api/-/api-1.0.0-beta.25.tgz#c617f4d38598a42b05c06caee1ce3769c7c10bbd"
integrity sha512-o6pbUnRGYTrTuH9OBiSbJg4A5KpbLYYXDb9SUYTMQAgRDhvDL/NQ+1YwzwTc42mj5hdH2pq9KKtgn5h3ghvgJQ==
"@polkadot/api@^1.0.0-beta.26":
version "1.0.0-beta.26"
resolved "https://registry.yarnpkg.com/@polkadot/api/-/api-1.0.0-beta.26.tgz#4d7b0f6665f16259b999595c8c979f0cdf6d7330"
integrity sha512-jHnZUak2a3XNj57aEdjW/9G5OHIptZAKOZavxoWzZ7yFL1FEbL8+O7r8pLfAs1mhLUS8LwQ+zTw+NhqnPa+gHw==
dependencies:
"@babel/runtime" "^7.8.3"
"@polkadot/api-derive" "^1.0.0-beta.25"
"@polkadot/api-derive" "^1.0.0-beta.26"
"@polkadot/keyring" "^2.0.0-beta.6"
"@polkadot/metadata" "^1.0.0-beta.25"
"@polkadot/rpc-core" "^1.0.0-beta.25"
"@polkadot/rpc-provider" "^1.0.0-beta.25"
"@polkadot/types" "^1.0.0-beta.25"
"@polkadot/metadata" "^1.0.0-beta.26"
"@polkadot/rpc-core" "^1.0.0-beta.26"
"@polkadot/rpc-provider" "^1.0.0-beta.26"
"@polkadot/types" "^1.0.0-beta.26"
"@polkadot/util-crypto" "^2.0.0-beta.6"
"@polkadot/dev-react@^0.32.17":
@ -2329,10 +2329,10 @@
dependencies:
"@babel/runtime" "^7.8.3"
"@polkadot/jsonrpc@^1.0.0-beta.25":
version "1.0.0-beta.25"
resolved "https://registry.yarnpkg.com/@polkadot/jsonrpc/-/jsonrpc-1.0.0-beta.25.tgz#e7384c0f70fa77cc858ed75c78da4e8ae0ddf3b4"
integrity sha512-3kGaqlGxQSMFRcjI1zjAhtixl5IrJmcGOY2D5gSqqzMOpVAYCi8K+n0mLbqVp28uewxMP/LsAcw9RyY4+vpPgQ==
"@polkadot/jsonrpc@^1.0.0-beta.26":
version "1.0.0-beta.26"
resolved "https://registry.yarnpkg.com/@polkadot/jsonrpc/-/jsonrpc-1.0.0-beta.26.tgz#3e7f062ce00d2d09bf9886f3616a5f8551381a6b"
integrity sha512-k5so4Mr4nTQTb3wf34YF1XD/J5tcnZPZfFIkWGhHbH2U9P45t73qPCpS6vLDLQQr0aC/HdzTMMEEs0qPkgy7YQ==
dependencies:
"@babel/runtime" "^7.8.3"
@ -2345,13 +2345,13 @@
"@polkadot/util" "^2.0.0-beta.6"
"@polkadot/util-crypto" "^2.0.0-beta.6"
"@polkadot/metadata@^1.0.0-beta.25":
version "1.0.0-beta.25"
resolved "https://registry.yarnpkg.com/@polkadot/metadata/-/metadata-1.0.0-beta.25.tgz#24a5fbcd8285fc25283fc4bccda3c32953f41edc"
integrity sha512-BGbTdETxseoktUuelhOlDwU3FZfNSDyyfrnNrMIltMQZGbr6i3awJRcxiXufyyuNjdvtcmdblUHQeFX4lv//Mg==
"@polkadot/metadata@^1.0.0-beta.26":
version "1.0.0-beta.26"
resolved "https://registry.yarnpkg.com/@polkadot/metadata/-/metadata-1.0.0-beta.26.tgz#5a8e0203d54be3a91955d23fee979f729c0ce756"
integrity sha512-lwwGbF+T7gbIgb+AuCX0G/6oOQrsx9gwzjifs4l1zLJPTVmhd6PLnrtO8ydp8f6jqlC+XqENkuOpAo/3t5Z95Q==
dependencies:
"@babel/runtime" "^7.8.3"
"@polkadot/types" "^1.0.0-beta.25"
"@polkadot/types" "^1.0.0-beta.26"
"@polkadot/util" "^2.0.0-beta.6"
"@polkadot/util-crypto" "^2.0.0-beta.6"
@ -2379,25 +2379,25 @@
qrcode-generator "^1.4.4"
react-qr-reader "^2.2.1"
"@polkadot/rpc-core@^1.0.0-beta.25":
version "1.0.0-beta.25"
resolved "https://registry.yarnpkg.com/@polkadot/rpc-core/-/rpc-core-1.0.0-beta.25.tgz#d80b07ebe663c493236dfa3022c70ab3e240a402"
integrity sha512-aq/Cx+FTT9Zw6mob47hKGXz8rcg2T35rvnIdgAULgMTjESti16pa/Pa4iQfMSV0l666MZ/JViUmDksIpiAljDg==
"@polkadot/rpc-core@^1.0.0-beta.26":
version "1.0.0-beta.26"
resolved "https://registry.yarnpkg.com/@polkadot/rpc-core/-/rpc-core-1.0.0-beta.26.tgz#4c630c1c162a4f67a0cd6d4ac518d0b8131d1cfc"
integrity sha512-aVVRbieI8GvoVIRHwqPX0A3qR+8mcaDaaYTv1HSTPZjd3zl1Io94w2fj32ow9LKAS12UmpHZ3CiRbQfZ+rxoFA==
dependencies:
"@babel/runtime" "^7.8.3"
"@polkadot/jsonrpc" "^1.0.0-beta.25"
"@polkadot/rpc-provider" "^1.0.0-beta.25"
"@polkadot/types" "^1.0.0-beta.25"
"@polkadot/jsonrpc" "^1.0.0-beta.26"
"@polkadot/rpc-provider" "^1.0.0-beta.26"
"@polkadot/types" "^1.0.0-beta.26"
"@polkadot/util" "^2.0.0-beta.6"
rxjs "^6.5.4"
"@polkadot/rpc-provider@^1.0.0-beta.25":
version "1.0.0-beta.25"
resolved "https://registry.yarnpkg.com/@polkadot/rpc-provider/-/rpc-provider-1.0.0-beta.25.tgz#afd73cd8cd4f5060ee3d16f519566fba5115be32"
integrity sha512-aYEEYblJMbCcLsD6UgcUooD3S7AJHb9/BeXYaMVy1u6+BfYZApxNxcrF8w7Y7IXpgguPSgtQXBstsAo6RUfijg==
"@polkadot/rpc-provider@^1.0.0-beta.26":
version "1.0.0-beta.26"
resolved "https://registry.yarnpkg.com/@polkadot/rpc-provider/-/rpc-provider-1.0.0-beta.26.tgz#87f9ca7be8ba4fc16c0381659d46b4d32d925c13"
integrity sha512-RWPFpTXAgK7cc3uubKURJUonAW9KlgGDLBEU4fdBOMjiPL0x8OIH84hFT0XpAzL2zwVaWFW5ymP9KT10g7LhNg==
dependencies:
"@babel/runtime" "^7.8.3"
"@polkadot/metadata" "^1.0.0-beta.25"
"@polkadot/metadata" "^1.0.0-beta.26"
"@polkadot/util" "^2.0.0-beta.6"
"@polkadot/util-crypto" "^2.0.0-beta.6"
eventemitter3 "^4.0.0"
@ -2411,13 +2411,13 @@
dependencies:
"@types/chrome" "^0.0.92"
"@polkadot/types@^1.0.0-beta.25":
version "1.0.0-beta.25"
resolved "https://registry.yarnpkg.com/@polkadot/types/-/types-1.0.0-beta.25.tgz#ae9a49dcabce7b518b91f0718cc7941c14c91bd3"
integrity sha512-Ut0Vg0r2NM96eU8iXQh6HXH68OnPXBv2P1wysDxuFHWvQ/xjEu8LxWtwOTqQzb5UqhL1FdUvSxTKfXVGh5MDgg==
"@polkadot/types@^1.0.0-beta.26":
version "1.0.0-beta.26"
resolved "https://registry.yarnpkg.com/@polkadot/types/-/types-1.0.0-beta.26.tgz#9c8da259b9be9bf40ed22ca6de955cbb85c9049b"
integrity sha512-KdUzbREXaoLhh5iG2qHd/yjdTdVJuK8tbBEZ1rfuwF1BVS1EzbwqrYn5y2QOKUlaqUMo0y3re729qiFnKurQjg==
dependencies:
"@babel/runtime" "^7.8.3"
"@polkadot/metadata" "^1.0.0-beta.25"
"@polkadot/metadata" "^1.0.0-beta.26"
"@polkadot/util" "^2.0.0-beta.6"
"@polkadot/util-crypto" "^2.0.0-beta.6"
"@types/memoizee" "^0.4.3"