Browse Source

Display turnout for poll (#3223)

Jaco Greeff 4 years ago
parent
commit
826598c1b1
3 changed files with 45 additions and 8 deletions
  1. 1 1
      README.md
  2. 1 0
      packages/page-accounts/src/Accounts/modals/Qr.tsx
  3. 43 7
      packages/page-poll/src/index.tsx

+ 1 - 1
README.md

@@ -8,7 +8,7 @@ A Portal into the Polkadot and Substrate networks. Provides a view and interacti
 
 This can be accessed as a hosted application via https://polkadot.js.org/apps/ or you can access the IPFS hosted version via https://polkadot.js.org/apps/ipfs (via hash) or https://dotapps.io (via ipns) to explore any of the supported Polkadot and Substrate chains.
 
-If you run one or more IPFS node(s), pinning the UI (which only getes updated on releases) will make it faster for you and others. You can find details about that below in the IPFS chapter below.
+If you run one or more IPFS node(s), pinning the UI (which only gets updated on releases) will make it faster for you and others. You can find details about that below in the IPFS chapter below.
 
 **Important** If you are a chain developer and would like to add support for your chain to the UI, all the local configuration (API types, settings, logos) can be customized in [the apps-config package](packages/apps-config#README.md), complete with instructions of what goes where.
 

+ 1 - 0
packages/page-accounts/src/Accounts/modals/Qr.tsx

@@ -51,6 +51,7 @@ function QrModal ({ className = '', onClose, onStatusChange }: Props): React.Rea
   const _onScan = useCallback(
     (scanned: Scanned): void => {
       setScanned(scanned);
+
       const { content, genesisHash } = scanned;
 
       setIsAddress(scanned.isAddress);

+ 43 - 7
packages/page-poll/src/index.tsx

@@ -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 { Balance, BlockNumber } from '@polkadot/types/interfaces';
+import { Approvals, Balance, BlockNumber } from '@polkadot/types/interfaces';
 import { ITuple } from '@polkadot/types/types';
 
 import BN from 'bn.js';
@@ -12,7 +12,7 @@ import styled from 'styled-components';
 import { Button, Columar, InputAddress, Progress, Spinner, Toggle, TxButton } from '@polkadot/react-components';
 import { useApi, useCall } from '@polkadot/react-hooks';
 import { FormatBalance, BlockToTime } from '@polkadot/react-query';
-import { BN_ONE, BN_ZERO, bnMax, formatNumber } from '@polkadot/util';
+import { BN_ONE, BN_ZERO, bnMax, formatBalance, formatNumber } from '@polkadot/util';
 
 import { useTranslation } from './translate';
 
@@ -20,6 +20,11 @@ interface Props {
   className?: string;
 }
 
+interface Turnout {
+  percentage: number;
+  voted: BN;
+}
+
 const DIV = new BN(1_000_000);
 
 function PollApp ({ className }: Props): React.ReactElement<Props> {
@@ -27,7 +32,9 @@ function PollApp ({ className }: Props): React.ReactElement<Props> {
   const { api } = useApi();
   const totals = useCall<ITuple<[Balance, Balance, Balance, Balance]>>(api.query.poll.totals, []);
   const bestNumber = useCall<BlockNumber>(api.derive.chain.bestNumber, []);
+  const totalIssuance = useCall<Balance>(api.query.balances.totalIssuance, []);
   const [accountId, setAccountId] = useState<string | null>(null);
+  const [turnout, setTurnout] = useState<Turnout | null>(null);
   const [opt10m, setOpt10m] = useState(false);
   const [opt100m, setOpt100m] = useState(false);
   const [opt1b, setOpt1b] = useState(false);
@@ -35,12 +42,22 @@ function PollApp ({ className }: Props): React.ReactElement<Props> {
   const [progress, setProgress] = useState<BN[] | undefined>();
 
   useEffect((): void => {
-    if (totals) {
+    if (totalIssuance && totals) {
       const max = bnMax(BN_ONE, ...totals);
 
       setProgress(totals.map((total) => total.mul(DIV).div(max)));
+
+      api.query.poll.voteOf
+        .entries<ITuple<[Approvals, Balance]>>()
+        .then((entries): void => {
+          const voted = entries.reduce((voted: BN, [, [, balance]]) => voted.iadd(balance), new BN(0));
+          const percentage = voted.muln(10_000).div(totalIssuance).toNumber() / 100;
+
+          setTurnout({ percentage, voted });
+        })
+        .catch(console.log);
     }
-  }, [totals]);
+  }, [api, totalIssuance, totals]);
 
   if (!totals || !progress || !bestNumber) {
     return (
@@ -52,6 +69,8 @@ function PollApp ({ className }: Props): React.ReactElement<Props> {
     );
   }
 
+  console.error(turnout);
+
   const blocksLeft = (api.consts.poll.end as BlockNumber).sub(bestNumber);
   const canVote = blocksLeft.gt(BN_ZERO);
   const options: [string, string, boolean, (value: boolean) => void][] = [
@@ -70,8 +89,16 @@ function PollApp ({ className }: Props): React.ReactElement<Props> {
         <div className='pollHeader'>
           <h1>{t('denomination vote')}</h1>
           <div className='pollBlocksRight'>
-            {canVote && <BlockToTime blocks={blocksLeft} />}
-            <div>#{formatNumber(api.consts.poll.end as BlockNumber)}</div>
+            {turnout && (
+              <div>
+                <div>{t('{{balance}} voted', { replace: { balance: formatBalance(turnout.voted) } })}</div>
+                <div>{t('{{percentage}}% turnout', { replace: { percentage: turnout.percentage.toFixed(2) } })}</div>
+              </div>
+            )}
+            <div>
+              {canVote && <BlockToTime blocks={blocksLeft} />}
+              <div>#{formatNumber(api.consts.poll.end as BlockNumber)}</div>
+            </div>
           </div>
         </div>
         <article className='keepAlive'>
@@ -169,10 +196,19 @@ export default React.memo(styled(PollApp)`
 
   .pollBlocksRight {
     position: absolute;
-    right: 0.5rem;
+    right: 0;
     text-align: right;
     opacity: 0.75;
     bottom: 0;
+
+    > div {
+      display: inline-block;
+      padding: 0 0.75rem;
+
+      &+div {
+        border-left: 1px solid #bbb;
+      }
+    }
   }
 
   .pollContainer {