Payout button for society (#4890)

* Add society payout button

* Split kusama design modal

* payout tag

* Adjust design size
This commit is contained in:
Jaco Greeff 2021-03-21 14:50:42 +01:00 committed by GitHub
parent cdbb596656
commit 5a5ec76eb8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 129 additions and 57 deletions

View File

@ -0,0 +1,77 @@
// Copyright 2017-2021 @polkadot/app-society authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { AccountId } from '@polkadot/types/interfaces';
import React, { useEffect, useRef, useState } from 'react';
import { KUSAMA_GENESIS } from '@polkadot/apps-config';
import { Button, Modal } from '@polkadot/react-components';
import { useApi, useToggle } from '@polkadot/react-hooks';
import drawCanary, { PADD, SIZE } from '../draw/canary';
import { useTranslation } from '../translate';
interface Props {
accountId: AccountId;
}
const CANVAS_STYLE = {
display: 'block',
margin: '0 auto'
};
const HEIGHT = (SIZE * 2) + (PADD * 1);
const WIDTH = (SIZE * 3) + (PADD * 2);
function DesignKusama ({ accountId }: Props): React.ReactElement<Props> | null {
const { t } = useTranslation();
const { api } = useApi();
const canvasRef = useRef<HTMLCanvasElement | null>(null);
const [onKusama] = useState(() => api.genesisHash.eq(KUSAMA_GENESIS));
const [isShowing, toggleDesign] = useToggle();
useEffect((): void => {
if (canvasRef.current) {
const ctx = canvasRef.current.getContext('2d');
if (ctx) {
drawCanary(ctx, accountId);
}
}
});
if (!onKusama) {
return null;
}
return (
<>
<Button
icon='pen-nib'
onClick={toggleDesign}
/>
{isShowing && (
<Modal
header={t('design samples')}
size='large'
>
<Modal.Content>
<canvas
height={HEIGHT}
ref={canvasRef}
style={CANVAS_STYLE}
width={WIDTH}
/>
</Modal.Content>
<Modal.Actions
cancelLabel={t<string>('Close')}
onCancel={toggleDesign}
/>
</Modal>
)}
</>
);
}
export default React.memo(DesignKusama);

View File

@ -5,17 +5,16 @@ import type BN from 'bn.js';
import type { Balance, BlockNumber } from '@polkadot/types/interfaces';
import type { MapMember } from '../types';
import React, { useCallback, useEffect, useRef, useState } from 'react';
import React, { useCallback, useMemo } from 'react';
import styled from 'styled-components';
import { KUSAMA_GENESIS } from '@polkadot/apps-config';
import { AddressSmall, Columar, Expander, Icon, Modal, Tag } from '@polkadot/react-components';
import { useApi, useToggle } from '@polkadot/react-hooks';
import { AddressSmall, Columar, Expander, Tag, TxButton } from '@polkadot/react-components';
import { useAccounts, useApi } from '@polkadot/react-hooks';
import { BlockToTime, FormatBalance } from '@polkadot/react-query';
import { formatNumber } from '@polkadot/util';
import drawCanary from '../draw/canary';
import { useTranslation } from '../translate';
import DesignKusama from './DesignKusama';
interface Props {
bestNumber?: BN;
@ -23,51 +22,53 @@ interface Props {
value: MapMember;
}
const CANVAS_STYLE = {
display: 'block',
margin: '0 auto'
};
function renderJSXPayouts (bestNumber: BN, payouts: [BlockNumber, Balance][]): JSX.Element[] {
return payouts.map(([bn, value], index) => (
<p key={index}>
<div
className='payout'
key={index}
>
<Columar>
<Columar.Column>
<FormatBalance value={value} />
</Columar.Column>
<Columar.Column>
<div>#{formatNumber(bn)}</div>
<BlockToTime
key={index}
value={bn.sub(bestNumber)}
/>
{bn.gt(bestNumber) && (
<BlockToTime
key={index}
value={bn.sub(bestNumber)}
/>
)}
</Columar.Column>
</Columar>
</p>
</div>
));
}
function Member ({ bestNumber, className = '', value: { isCandidateVoter, isFounder, isHead, isSkeptic, isWarned, member: { accountId, isDefenderVoter, isSuspended, payouts, strikes } } }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const { api } = useApi();
const canvasRef = useRef<HTMLCanvasElement | null>(null);
const [canInk] = useState(() => api.genesisHash.eq(KUSAMA_GENESIS));
const [isInkShowing, toggleInk] = useToggle();
const { allAccounts } = useAccounts();
const renderPayouts = useCallback(
() => bestNumber && payouts && renderJSXPayouts(bestNumber, payouts),
[bestNumber, payouts]
);
useEffect((): void => {
if (canvasRef.current) {
const ctx = canvasRef.current.getContext('2d');
const isMember = useMemo(
(): boolean => {
const address = accountId.toString();
if (ctx) {
drawCanary(ctx, accountId);
}
}
});
return allAccounts.some((accountId) => accountId === address);
},
[allAccounts, accountId]
);
const availablePayout = useMemo(
() => bestNumber && payouts.find(([bn]) => bestNumber.gt(bn)),
[bestNumber, payouts]
);
return (
<tr className={className}>
@ -101,7 +102,7 @@ function Member ({ bestNumber, className = '', value: { isCandidateVoter, isFoun
)}
{isWarned && (
<Tag
color='grey'
color='orange'
label={t<string>('strikes')}
/>
)}
@ -111,6 +112,12 @@ function Member ({ bestNumber, className = '', value: { isCandidateVoter, isFoun
label={t<string>('suspended')}
/>
)}
{availablePayout && (
<Tag
color='grey'
label={t<string>('payout')}
/>
)}
</td>
<td className='number together'>
{!!payouts?.length && (
@ -123,33 +130,17 @@ function Member ({ bestNumber, className = '', value: { isCandidateVoter, isFoun
<td>{isDefenderVoter && t<string>('defender')}</td>
<td>{isCandidateVoter && t<string>('candidate')}</td>
<td className='number'>{formatNumber(strikes)}</td>
<td>
{canInk && (
<>
<Icon
icon='pen-nib'
onClick={toggleInk}
/>
{isInkShowing && (
<Modal
header={t('design samples')}
size='large'
>
<Modal.Content>
<canvas
height={525}
ref={canvasRef}
style={CANVAS_STYLE}
width={800}
/>
</Modal.Content>
<Modal.Actions
cancelLabel={t<string>('Close')}
onCancel={toggleInk}
/>
</Modal>
)}
</>
<td className='button start'>
<DesignKusama accountId={accountId} />
{availablePayout && (
<TxButton
accountId={accountId}
icon='ellipsis-h'
isDisabled={!isMember}
label='Payout'
params={[]}
tx={api.tx.society.payout}
/>
)}
</td>
</tr>
@ -157,6 +148,10 @@ function Member ({ bestNumber, className = '', value: { isCandidateVoter, isFoun
}
export default React.memo(styled(Member)`
.payout+.payout {
margin-top: 0.5rem;
}
.ui--Column {
min-width: 14ch;

View File

@ -6,8 +6,8 @@
import type { AccountId } from '@polkadot/types/interfaces';
// const DEFAULT_FN = (ctx: CanvasRenderingContext2D, path: Path2D) => ctx.fill(path);
const PADD = 25;
const SIZE = 250;
export const PADD = 25;
export const SIZE = 300; // 250
function canary (ctx: CanvasRenderingContext2D, w: number, h: number, s: number, f: (ctx: CanvasRenderingContext2D, path: Path2D) => void): void {
const path = new Path2D('M373.1,126.9c-5.2-4.1-11.4-9.7-22.7-11.1c-10.6-1.4-21.4,5.7-28.7,10.4c-7.3,4.7-21.1,18.5-26.8,22.7 c-5.7,4.2-20.3,8.1-43.8,22.2s-115.7,73.3-115.7,73.3l24,0.3L52.4,299.8h10.7l-15.4,11.7c0,0,13.6,3.6,25-3.6l0,3.3 c0,0,127.4-50.2,152-37.2l-15,4.4c1.3,0,25.5,1.6,25.5,1.6s0.8,15.1,15.4,24.8c14.6,9.6,14.9,14.9,14.9,14.9s-7.6,3.1-7.6,7 c0,0,11.2-3.4,21.6-3.1c10.4,0.3,19.5,3.1,19.5,3.1s-0.8-4.2-10.9-7c-10.2-2.9-20.1-13.8-25-19.8c-4.9-6-8.3-16.7-4.1-27.4 c3.5-9.1,15.7-14.1,40.9-27.1c29.7-15.4,36.5-26.8,40.7-35.7c4.2-8.9,10.4-26.6,13.9-34.9c4.4-10.7,9.8-16.4,14.3-19.8 c4.4-3.4,24.5-10.9,24.5-10.9S378,130.8,373.1,126.9z');