Bulk media tips (#3535)

* Bulk media tips

* lint
This commit is contained in:
Jaco Greeff 2020-08-27 11:03:54 +02:00 committed by GitHub
parent 2f4e0fbafd
commit ea63c8eb53
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 89 additions and 19 deletions

View File

@ -149,7 +149,7 @@ function Validator ({ allSlashes, canSelect, filterName, info, isNominated, isSe
<td className='number together'>{!bondOwn.isZero() && <FormatBalance value={bondOwn} />}</td>
<td className='number together media--1600'>{!bondOther.isZero() && <FormatBalance value={bondOther} />}</td>
<td className='number together'>{!rewardPayout.isZero() && <FormatBalance value={rewardPayout} />}</td>
<td>
<td className='badge'>
{(canSelect || isSelected) && (
<Checkbox
onChange={_toggleSelected}

View File

@ -140,6 +140,7 @@ function Targets ({ className = '', isInElection, ownStashes, targets: { avgStak
1,
(): void => _sort(header as 'rankComm')
]),
[undefined, 'badge'],
[]
], [_sort, labels, sortBy, sorted, sortFromMax, t]);

View File

@ -5,9 +5,9 @@
import { AccountId, Balance, BlockNumber, OpenTip, OpenTipTo225 } from '@polkadot/types/interfaces';
import BN from 'bn.js';
import React, { useMemo } from 'react';
import React, { useEffect, useMemo, useState } from 'react';
import styled from 'styled-components';
import { AddressSmall, AddressMini, Expander, Icon, LinkExternal, TxButton } from '@polkadot/react-components';
import { AddressSmall, AddressMini, Checkbox, Expander, Icon, LinkExternal, TxButton } from '@polkadot/react-components';
import { useAccounts } from '@polkadot/react-hooks';
import { BlockToTime, FormatBalance } from '@polkadot/react-query';
import { BN_ZERO, formatNumber } from '@polkadot/util';
@ -20,9 +20,11 @@ import TipReason from './TipReason';
interface Props {
bestNumber?: BlockNumber;
className?: string;
defaultId: string | null;
hash: string;
isMember: boolean;
members: string[];
onSelect: (hash: string, isSelected: boolean, value: BN) => void,
tip: OpenTip | OpenTipTo225;
}
@ -74,7 +76,7 @@ function extractTipState (tip: OpenTip | OpenTipTo225, hash: string, allAccounts
};
}
function Tip ({ bestNumber, className = '', hash, isMember, members, tip }: Props): React.ReactElement<Props> | null {
function Tip ({ bestNumber, className = '', defaultId, hash, isMember, members, onSelect, tip }: Props): React.ReactElement<Props> | null {
const { t } = useTranslation();
const { allAccounts } = useAccounts();
@ -83,6 +85,16 @@ function Tip ({ bestNumber, className = '', hash, isMember, members, tip }: Prop
[allAccounts, hash, tip]
);
const [isMedianSelected, setMedianTip] = useState(false);
useEffect((): void => {
onSelect(hash, isMedianSelected, median);
}, [hash, isMedianSelected, median, onSelect]);
useEffect((): void => {
setMedianTip(isMember && !isTipper);
}, [isMember, isTipper]);
const { reason, tips, who } = tip;
return (
@ -135,6 +147,7 @@ function Tip ({ bestNumber, className = '', hash, isMember, members, tip }: Prop
{(!closesAt || !bestNumber || closesAt.gt(bestNumber))
? (
<TipEndorse
defaultId={defaultId}
hash={hash}
isMember={isMember}
isTipped={isTipped}
@ -159,6 +172,13 @@ function Tip ({ bestNumber, className = '', hash, isMember, members, tip }: Prop
/>
)}
</td>
<td className='badge'>
<Checkbox
isDisabled={!isMember}
onChange={setMedianTip}
value={isMedianSelected}
/>
</td>
<td className='mini media--1700'>
<LinkExternal
data={hash}

View File

@ -3,13 +3,14 @@
// of the Apache-2.0 license. See the LICENSE file for details.
import BN from 'bn.js';
import React, { useEffect, useState } from 'react';
import React, { useState } from 'react';
import { Button, InputAddress, InputBalance, Modal, TxButton } from '@polkadot/react-components';
import { useAccounts, useToggle } from '@polkadot/react-hooks';
import { useToggle } from '@polkadot/react-hooks';
import { useTranslation } from '../translate';
interface Props {
defaultId: string | null;
hash: string;
isMember: boolean;
isTipped: boolean;
@ -17,20 +18,12 @@ interface Props {
members: string[];
}
function TipEndorse ({ hash, isMember, isTipped, median, members }: Props): React.ReactElement<Props> {
function TipEndorse ({ defaultId, hash, isMember, isTipped, median, members }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const [isOpen, toggleOpen] = useToggle();
const { allAccounts } = useAccounts();
const [accountId, setAccountId] = useState<string | null>(null);
const [defaultId, setDefaultId] = useState<string | null>(null);
const [accountId, setAccountId] = useState<string | null>(defaultId);
const [value, setValue] = useState<BN | undefined>();
useEffect((): void => {
setDefaultId(
members.find((memberId) => allAccounts.includes(memberId)) || null
);
}, [allAccounts, members]);
return (
<>
<Button

View File

@ -4,6 +4,7 @@
import { BlockNumber, OpenTip, OpenTipTo225 } from '@polkadot/types/interfaces';
import BN from 'bn.js';
import React, { useMemo, useRef } from 'react';
import { Table } from '@polkadot/react-components';
import { useApi, useCall } from '@polkadot/react-hooks';
@ -14,9 +15,11 @@ import Tip from './Tip';
interface Props {
className?: string;
defaultId: string | null;
hashes?: string[] | null;
isMember: boolean;
members: string[];
onSelectTip: (hash: string, isSelected: boolean, value: BN) => void,
}
type Tip = [string, OpenTip | OpenTipTo225];
@ -40,7 +43,7 @@ function extractTips (optTips?: Option<OpenTip>[], hashes?: string[] | null): Ti
);
}
function Tips ({ className = '', hashes, isMember, members }: Props): React.ReactElement<Props> {
function Tips ({ className = '', defaultId, hashes, isMember, members, onSelectTip }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const { api } = useApi();
const bestNumber = useCall<BlockNumber>(api.derive.chain.bestNumber);
@ -58,6 +61,7 @@ function Tips ({ className = '', hashes, isMember, members }: Props): React.Reac
[],
[],
[undefined, 'badge media--1700'],
[undefined, 'badge'],
[undefined, 'mini media--1700']
]);
@ -70,10 +74,12 @@ function Tips ({ className = '', hashes, isMember, members }: Props): React.Reac
{tips?.map(([hash, tip]): React.ReactNode => (
<Tip
bestNumber={bestNumber}
defaultId={defaultId}
hash={hash}
isMember={isMember}
key={hash}
members={members}
onSelect={onSelectTip}
tip={tip}
/>
))}

View File

@ -2,9 +2,14 @@
// 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 { Button } from '@polkadot/react-components';
import { SubmittableExtrinsic } from '@polkadot/api/types';
import BN from 'bn.js';
import React, { useCallback, useMemo, useState } from 'react';
import { Button, TxButton } from '@polkadot/react-components';
import { useAccounts, useApi } from '@polkadot/react-hooks';
import { useTranslation } from '../translate';
import TipCreate from './TipCreate';
import Tips from './Tips';
@ -16,7 +21,43 @@ interface Props {
trigger: () => void;
}
interface QuickTipsState {
quickTips: Record<string, BN | null>;
quickTx: SubmittableExtrinsic<'promise'> | null;
}
function TipsEntry ({ className, hashes, isMember, members, trigger }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const { allAccounts } = useAccounts();
const { api } = useApi();
const [{ quickTx }, setQuickTips] = useState<QuickTipsState>({ quickTips: {}, quickTx: null });
const defaultId = useMemo(
() => members.find((memberId) => allAccounts.includes(memberId)) || null,
[allAccounts, members]
);
const _selectTip = useCallback(
(hash: string, isSelected: boolean, value: BN) => setQuickTips(({ quickTips }): QuickTipsState => {
quickTips[hash] = isSelected ? value : null;
const available = Object
.entries(quickTips)
.map(([hash, value]) => value ? api.tx.treasury.tip(hash, value) : null)
.filter((value): value is SubmittableExtrinsic<'promise'> => !!value);
return {
quickTips,
quickTx: available.length
? available.length === 1
? available[0]
: api.tx.utility.batch(available)
: null
};
}),
[api]
);
return (
<div className={className}>
<Button.Group>
@ -24,11 +65,20 @@ function TipsEntry ({ className, hashes, isMember, members, trigger }: Props): R
members={members}
refresh={trigger}
/>
<TxButton
accountId={defaultId}
extrinsic={quickTx}
icon='fighter-jet'
isDisabled={!isMember || !quickTx}
label={t<string>('Median tip selected')}
/>
</Button.Group>
<Tips
defaultId={defaultId}
hashes={hashes}
isMember={isMember}
members={members}
onSelectTip={_selectTip}
/>
</div>
);